<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Sveltekit Blog Engine</title>
        <link>https://testblog-6br.pages.dev</link>
        <description>The official blog for the free, serverless SvelteKit blog engine powered by Cloudflare D1, KV, and Pages.</description>
        <language>en</language>
        <atom:link href="https://testblog-6br.pages.dev/en/rss.xml" rel="self" type="application/rss+xml"/>
        <lastBuildDate>Tue, 22 Sep 2026 18:16:36 GMT</lastBuildDate>
        <item>
            <title><![CDATA[SKBE v1.0.1.0 Update: Independent Design Presets, Responsive Sidebar Stacking, and i18n Perfection]]></title>
            <link>https://testblog-6br.pages.dev/en/devlog/skbe-v1010-design-preset-backup-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/devlog/skbe-v1010-design-preset-backup-guide</guid>
            <pubDate>Sun, 20 Sep 2026 23:18:38 GMT</pubDate>
            <description><![CDATA[Here is a breakdown of the SvelteKit Blog Engine (SKBE) v1.0.1.0 release. We introduce an isolated slot preset backup architecture that prevents database overwrites, responsive full-width sidebar layout controls, proper CJK/Japanese text wrapping with dynamic slot name localization, and automated deployment script synchronization.]]></description>
            <content:encoded><![CDATA[<h2 id="1-introduction-operational-challenges-identified-after-v10015">1. Introduction: Operational Challenges Identified After v1.0.0.15</h2>
<p>Following the release of SvelteKit Blog Engine (SKBE) v1.0.0.15—which introduced the O(1) D1 sidebar snapshot cache and the media URL migration pipeline—we spent time actively operating the admin console across diverse environments, screen resolutions, and languages (Korean, English, and Japanese). During real-world content creation and theme adjustments, several architectural bottlenecks and subtle layout defects came to light.</p>
<p>The v1.0.1.0 update focuses directly on three pillars: <strong>design editing safety</strong>, <strong>responsive visual polish in multilingual UI</strong>, and <strong>streamlined deployment automation</strong>:</p>
<ol>
<li><strong>Eliminating Destructive Database Overwrites in Design Backups</strong>: Replaced the legacy full-table drop/replace restore mechanism with an isolated, neutral JSON preset format (<code>design_preset</code>) that backs up individual slots and injects them safely into targeted slots.</li>
<li><strong>Fixing Sidebar Element Overflow &amp; CJK Clipping</strong>: Resolved horizontal clipping and button cut-offs within narrow sidebars, alongside CJK typography issues where spaceless Japanese text failed to wrap correctly.</li>
<li><strong>Dynamic Slot Name Localization</strong>: Solved fixed Korean default names (&quot;디자인 슬롯 1&quot;) stored in the database by dynamically translating them to match the active admin language (<code>Design Slot 1</code>, <code>デザインスロット 1</code>) in real time.</li>
<li><strong>Automated Project Name Synchronization in Deploy Scripts</strong>: Added automated pipelines so that <code>package.json</code> deploy commands stay seamlessly synced with the resolved project names during initial setup and multi-account syncs.</li>
<li><strong>Real-Time Sidebar Snapshot Sync &amp; Dependency Security Patches</strong>: Linked post lifecycle events (create, edit, delete) directly to background D1 snapshot generation, and patched security vulnerabilities in core dependencies.</li>
</ol>
<hr>
<h2 id="2-design-editor-isolated-slot-preset-backup-amp-target-slot-restore-architecture">2. Design Editor: Isolated Slot Preset Backup &amp; Target Slot Restore Architecture</h2>
<h3 id="1-limitations-and-risks-of-the-legacy-approach">1) Limitations and Risks of the Legacy Approach</h3>
<p>Previously, the design backup and restore feature interacted with the <code>/api/restore</code> endpoint by executing a destructive sweep: it completely purged (<code>DELETE FROM</code>) the <code>layouts</code>, <code>widgets</code>, and <code>layout_widgets</code> tables and re-inserted records directly from the uploaded file.</p>
<p>This legacy flow carried serious risks:</p>
<ul>
<li><strong>Catastrophic Data Loss</strong>: Restoring a single backup file instantly wiped out every layout and widget configuration across all design slots (Slots 1, 2, and 3) without warning.</li>
<li><strong>No Slot Granularity</strong>: Administrators could not isolate a specific theme (such as Slot 2’s Minimalist 1-column layout) into a standalone preset file, nor could they safely import a custom preset from another blog into only Slot 3.</li>
<li><strong>Lack of Visual Verification Before Commit</strong>: Restoring immediately committed changes to the production database, leaving no opportunity to preview or rollback incorrect backup uploads.</li>
</ul>
<h3 id="2-the-new-architecture-neutral-design-preset-design-preset-pipeline">2) The New Architecture: Neutral Design Preset (<code>design_preset</code>) Pipeline</h3>
<p>We completely retired the full-database sweep and introduced an isolated, neutral preset architecture integrated directly with the editor’s reactive in-memory state (<code>slots</code>).</p>
<pre><code class="language-typescript">// Extracts pure visual and layout data as a neutral preset
const presetData = {
    version: &quot;3.0&quot;,
    backupType: &quot;design_preset&quot;,
    timestamp: new Date().toISOString(),
    presetName: slotName,
    design: {
        theme: snapshot.theme,
        header: snapshot.header,
        footer: snapshot.footer,
        site_title: snapshot.site_title,
        widget_shadow_global: snapshot.widget_shadow_global,
        layout: snapshot.layout,
        widgets: snapshot.widgets
    }
};
</code></pre>
<h4 id="key-advantages-of-the-new-preset-system">Key Advantages of the New Preset System</h4>
<ul>
<li><strong>Neutral Slot Association</strong>: Presets contain zero hardcoded slot identifiers (<code>slot1</code>, <code>slot2</code>, etc.). A preset exported from Slot 1 can be freely imported into Slot 2, Slot 3, or any future slot without conflict.</li>
<li><strong>Unsaved In-Memory State Retention</strong>: When exporting the currently active slot, the system captures real-time, unpersisted editor edits (<code>getSnapshotOfCurrentSlot()</code>), ensuring the download contains the exact state visible on screen.</li>
<li><strong>Preview-First 2-Stage Safe Injection</strong>: When a file is uploaded and a target slot is selected, the data is injected strictly into the editor&#39;s reactive memory state first. The live preview updates immediately, and changes are committed to D1 only when the user explicitly clicks <strong>[Apply to Blog]</strong> or <strong>[Save Current Settings]</strong>.</li>
<li><strong>Legacy Backup Compatibility (<code>convertLegacyBackupToPreset</code>)</strong>: An automated parser detects whether an imported file uses the new preset format or a legacy database table dump, seamlessly normalizing legacy data into the modern slot schema.</li>
</ul>
<hr>
<h2 id="3-responsive-sidebar-stacking-amp-multilingual-i18n-perfection">3. Responsive Sidebar Stacking &amp; Multilingual (i18n) Perfection</h2>
<h3 id="1-resolving-sidebar-button-clipping-with-full-width-stacking">1) Resolving Sidebar Button Clipping with Full-Width Stacking</h3>
<p>The administrator sidebar provides an effective content width of approximately 260px–290px after padding. Previously, slot select dropdowns and action buttons were placed side by side in a single horizontal row (<code>flex-row</code>).</p>
<p>Because the dropdown consumed default space, action buttons were pushed off the right boundary—clipping download button labels and pushing the restore confirmation button completely off-screen.</p>
<p>We restructured the layout into a <strong>responsive vertical stack (<code>flex-col</code>, <code>w-full</code>)</strong>:</p>
<pre><code class="language-html">&lt;!-- Backup section: full-width vertical stack --&gt;
&lt;div class=&quot;setting-control flex flex-col gap-2 w-full&quot;&gt;
    &lt;select class=&quot;select-field w-full&quot; bind:value={slotBackupTargetId}&gt;...&lt;/select&gt;
    &lt;button class=&quot;btn-primary w-full flex items-center justify-center gap-2&quot;&gt;
        &lt;Download size={16} /&gt;
        &lt;span&gt;Download Slot Backup&lt;/span&gt;
    &lt;/button&gt;
&lt;/div&gt;
</code></pre>
<p>With each control occupying its own 100% width row, buttons align neatly within the panel regardless of how narrow the sidebar becomes.</p>
<h3 id="2-debugging-japanese-typography-and-18-character-katakana-button-overflow">2) Debugging Japanese Typography and 18-Character Katakana Button Overflow</h3>
<p>During multilingual validation, we encountered visual defects that occurred exclusively in the Japanese locale. Thorough investigation revealed two distinct root causes:</p>
<h4 id="root-cause-a-spaceless-japanese-sentences-vs-word-break-keep-all">Root Cause A: Spaceless Japanese Sentences vs <code>word-break: keep-all</code></h4>
<ul>
<li><strong>Symptom</strong>: Hint descriptions (<code>各スロットのデザインを独立したファイルとしてエ...</code>) overflowed the container, truncating text on the right.</li>
<li><strong>Cause</strong>: Korean words are separated by spaces, allowing <code>keep-all</code> to wrap cleanly at word boundaries. However, <strong>written Japanese contains no spaces</strong>. When <code>keep-all</code> was applied, browsers treated entire sentences as single unbroken words and refused to wrap.</li>
<li><strong>Solution</strong>: Replaced <code>keep-all</code> with <code>overflow-wrap: anywhere; word-break: break-word;</code>, allowing Japanese characters to wrap naturally at any character boundary without spilling outside the container.</li>
</ul>
<h4 id="root-cause-b-legacy-css-width-auto-important-vs-18-character-katakana">Root Cause B: Legacy CSS <code>width: auto !important;</code> vs 18-Character Katakana</h4>
<ul>
<li><strong>Symptom</strong>: The Japanese backup button (<code>スロットバックアップをダウンロード</code>) protruded more than 50px past the white card border.</li>
<li><strong>Cause</strong>: A legacy CSS rule (<code>.setting-control .btn-primary { width: auto !important; }</code>) overrode our <code>w-full</code> class. While the Korean label (<code>슬롯 백업 다운로드</code>, 9 characters) was short enough to fit, the Japanese Katakana translation was 18 characters long. Under <code>width: auto !important;</code>, the button&#39;s intrinsic width expanded past 320px, far exceeding the 260px available sidebar width.</li>
<li><strong>Solution</strong>: Updated the CSS selector to <code>.setting-control .btn-primary:not(.w-full)</code> to lift the <code>!important</code> restriction, and applied <code>style=&quot;width: 100% !important; max-width: 100%; white-space: normal; word-break: break-word;&quot;</code> to ensure the button adapts cleanly to 100% width and wraps text internally when necessary.</li>
</ul>
<h3 id="3-real-time-dynamic-slot-name-localization-getslotdisplayname">3) Real-Time Dynamic Slot Name Localization (<code>getSlotDisplayName</code>)</h3>
<p>Because default slot names (&quot;디자인 슬롯 1&quot;, &quot;미니멀 1열&quot;, &quot;다크 모던&quot;) are persisted in D1 as Korean strings during initial setup, switching the admin interface to English or Japanese previously left Korean names inside the select options.</p>
<p>We introduced the <code>getSlotDisplayName</code> helper function. It preserves any custom names entered by users while dynamically translating standard defaults into the active language (<code>adminLang.value</code>):</p>
<ul>
<li><strong>Korean</strong>: <code>슬롯 1 (디자인 슬롯 1)</code></li>
<li><strong>English</strong>: <code>Slot 1 (Design Slot 1)</code></li>
<li><strong>Japanese</strong>: <code>スロット 1 (デザインスロット 1)</code></li>
</ul>
<hr>
<h2 id="4-deployment-amp-multi-account-automation-project-name-sync-pipeline">4. Deployment &amp; Multi-Account Automation: Project Name Sync Pipeline</h2>
<p>SKBE features a multi-account deployment architecture (<code>scripts/deploy-multi.js</code>) that allows blogs and admin consoles to be distributed across distinct Cloudflare accounts.</p>
<p>Previously, whether setting up a new instance manually or generating random project names automatically (<code>setup.js</code>), the <code>--project-name</code> arguments inside <code>package.json</code> deploy commands (<code>deploy:blog</code>, <code>deploy:admin</code>) had to be adjusted by hand.</p>
<p>We added the <code>updatePackageJsonDeployScripts</code> pipeline to <code>scripts/setup.js</code> and <code>scripts/sync-accounts.js</code>:</p>
<ul>
<li>The resolved project names automatically update inside <code>package.json</code> during both manual and automated installation flows.</li>
<li>Running <code>npm run deploy:sync</code> guarantees that the <code>main</code> account project names in <code>.deploy-accounts.json</code> and the scripts in <code>package.json</code> remain perfectly synchronized.</li>
</ul>
<hr>
<h2 id="5-engine-optimization-and-dependency-security">5. Engine Optimization and Dependency Security</h2>
<ol>
<li><strong>Real-Time Sidebar Snapshot Synchronization</strong>:<ul>
<li>The O(1) sidebar snapshot introduced in v1.0.0.15 is now triggered asynchronously (<code>generateSidebarSnapshot</code>) upon post creation (<code>new</code>), modification (<code>[id]</code>), and deletion/status changes (<code>posts</code>), keeping edge D1 caches fresh without blocking user responses.</li>
</ul>
</li>
<li><strong>Media URL Migration Path Broadening</strong>:<ul>
<li>Upgraded the regular expression and query in <code>/api/media/migrate-urls</code> to detect and rewrite proxy URLs stored in <code>posts/</code> subfolders or custom directory hierarchies.</li>
</ul>
</li>
<li><strong>Security Patches &amp; Zod v4 Version Pinning</strong>:<ul>
<li>Updated <code>better-auth</code> and <code>tiptap</code> packages to patched versions, and locked <code>zod</code> strictly to the v4 branch via pnpm <code>overrides</code> to ensure build reproducibility.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="6-architecture-comparison-before-vs-after">6. Architecture Comparison (Before vs After)</h2>
<table>
<thead>
<tr>
<th align="left">Feature</th>
<th align="left">Legacy (Prior to v1.0.1.0)</th>
<th align="left">Improved (v1.0.1.0)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Design Backup &amp; Restore</strong></td>
<td align="left">Full DB table wipe (<code>DELETE FROM</code>) destroying all slots</td>
<td align="left"><strong>Isolated neutral preset (<code>design_preset</code>) with target slot injection</strong></td>
</tr>
<tr>
<td align="left"><strong>Restore Verification</strong></td>
<td align="left">Immediate DB overwrite with no preview or rollback</td>
<td align="left"><strong>In-memory injection ➔ Live preview check ➔ Explicit user commit</strong></td>
</tr>
<tr>
<td align="left"><strong>Sidebar Layout</strong></td>
<td align="left">Single horizontal row; buttons clipped on narrow screens</td>
<td align="left"><strong>Responsive vertical stack (<code>flex-col</code>, <code>w-full</code>) with 100% width alignment</strong></td>
</tr>
<tr>
<td align="left"><strong>Japanese Typography</strong></td>
<td align="left"><code>keep-all</code> blocked wrapping; button protruded 50px+</td>
<td align="left"><strong><code>overflow-wrap: anywhere</code>, flexible multi-line button wrapping</strong></td>
</tr>
<tr>
<td align="left"><strong>Slot Name i18n</strong></td>
<td align="left">Hardcoded Korean strings from database persisted</td>
<td align="left"><strong>Dynamic real-time localization via <code>getSlotDisplayName</code></strong></td>
</tr>
<tr>
<td align="left"><strong>Deploy Command Sync</strong></td>
<td align="left">Manual editing required in <code>package.json</code></td>
<td align="left"><strong>Automated sync in <code>setup.js</code> and <code>sync-accounts.js</code></strong></td>
</tr>
</tbody></table>
<hr>
<h2 id="7-conclusion">7. Conclusion</h2>
<p>The v1.0.1.0 release is more than an incremental patch; it eliminates <strong>critical data loss risks (design overwrite)</strong> and refines <strong>visual and typographic polish across multilingual environments</strong>.</p>
<p>SKBE will continue to leverage the full power of Cloudflare&#39;s serverless edge ecosystem while maintaining an intuitive, dependable, and globally responsive authoring experience.</p>
]]></content:encoded>
            <category>DevLog</category>
        </item>
        <item>
            <title><![CDATA[SKBE v1.0.0.15 Update: D1 Snapshot Engine, Storage URL Migration, and SEO Optimization]]></title>
            <link>https://testblog-6br.pages.dev/en/devlog/skbe-engine-optimization-v15-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/devlog/skbe-engine-optimization-v15-guide</guid>
            <pubDate>Sun, 20 Sep 2026 06:29:56 GMT</pubDate>
            <description><![CDATA[A breakdown of SvelteKit Blog Engine (SKBE) v1.0.0.15 updates: introducing the sidebar snapshot engine to reduce complex D1 queries to O(1), an image URL migration pipeline to protect Cloudflare Workers free-tier limits, dynamic admin control for tag noindex, and SEO normalization.]]></description>
            <content:encoded><![CDATA[<h2 id="1-introduction-operational-inefficiencies-in-practice">1. Introduction: Operational Inefficiencies in Practice</h2>
<p>Following the stabilization of our multi-slot design system and the complete resolution of layout shifting (achieving CLS 0.000) across SKBE v1.0.0.11 through v1.0.0.14, putting the engine into day-to-day writing and real-world operation revealed several subtle inefficiencies and operational friction points.</p>
<p>Because this project is fundamentally designed around a zero-cost serverless architecture on Cloudflare&#39;s Free Tier (Pages, D1 SQLite, and KV), eliminating even minor query overhead and operational hassles pays significant dividends over the long run.</p>
<p>In v1.0.0.15, we focused our engineering efforts on addressing the following areas:</p>
<ol>
<li><strong>Repetitive Sidebar Widget Queries</strong>: Replacing redundant D1 queries executed on every page navigation for categories, recent posts, and popular posts.</li>
<li><strong>Workers Call Reduction &amp; Serving Optimization</strong>: Mitigating exhaustion of the daily Cloudflare Workers free tier (100,000 requests/day) by transitioning body image URLs from proxy endpoints (<code>/images/...</code>) to direct R2/Supabase endpoints.</li>
<li><strong>Semantic Layout Refactoring</strong>: Structuring columns and widgets with HTML5 semantic elements so search engine crawlers can clearly distinguish main content from peripheral sidebars.</li>
<li><strong>SEO Metadata Normalization &amp; Dynamic Tag noindex</strong>: Refining sitemap and Open Graph specifications, while introducing an admin toggle to control tag page indexing on the fly without redeployment.</li>
</ol>
<hr>
<h2 id="2-slashing-d1-read-queries-the-sidebar-snapshot-engine">2. Slashing D1 Read Queries: The Sidebar Snapshot Engine</h2>
<h3 id="1-the-problem">1) The Problem</h3>
<p>In the global blog layout (<code>+layout.server.ts</code>), every route request must assemble data for the sidebar: category lists, recent posts, and popular posts.</p>
<p>Previously, all three queries were executed against the Cloudflare D1 database on every single page navigation. As visitor traffic and pageviews grew, this consumed D1 read operations unnecessarily and added cumulative latency between the edge runtime and the database.</p>
<h3 id="2-implementation-static-serving-via-sidebar-snapshot">2) Implementation: Static Serving via <code>sidebar_snapshot</code></h3>
<p>We introduced <code>packages/shared/src/utils/snapshot.ts</code> to bundle category structures, recent posts, and popular posts into a single precomputed JSON snapshot, cached directly within the <code>sidebar_snapshot</code> column of the <code>blog_settings</code> table.</p>
<pre><code class="language-typescript">// apps/blog/src/routes/+layout.server.ts
const sidebarSnapshot = safeParse(settings?.sidebar_snapshot);
const hasSnapshot = sidebarSnapshot &amp;&amp; typeof sidebarSnapshot === &#39;object&#39; &amp;&amp; sidebarSnapshot.recentPosts;

if (hasSnapshot) {
    // Serve immediately from snapshot with zero DB queries
    categories = sidebarSnapshot.categories?.[currentLang] || sidebarSnapshot.categories?.[defaultLang] || [];
    recentPosts = rLimit ? rawRecent.slice(0, rLimit) : rawRecent;
    popularPosts = pLimit ? rawPopular.slice(0, pLimit) : rawPopular;
    
    // Only query frequently shifting widgets (e.g., tags, comments) as needed
} else {
    // If snapshot is missing, fetch once and generate snapshot in the background
    generateSidebarSnapshot(rawD1).catch(err =&gt; console.error(&#39;[Snapshot Background]&#39;, err));
}
</code></pre>
<ul>
<li><strong>Outcome</strong>: Standard page browsing now resolves sidebar content in $O(1)$ time with zero D1 overhead.</li>
<li>When posts are published or updated, the snapshot is regenerated asynchronously in the background, keeping reader response times instantaneous.</li>
</ul>
<hr>
<h2 id="3-protecting-cloudflare-workers-limits-post-image-url-migration">3. Protecting Cloudflare Workers Limits: Post Image URL Migration</h2>
<h3 id="1-context-proxy-bottlenecks-and-the-storage-dilemma">1) Context: Proxy Bottlenecks and the Storage Dilemma</h3>
<p>SKBE supports a variety of media backends, including ImageKit, Cloudflare R2, Supabase Storage, and Cloudflare KV.</p>
<p>From a purely architectural standpoint, <strong>Cloudflare R2 is the most natural fit</strong> due to its zero-egress fee model within the Cloudflare ecosystem. However, activating R2 strictly requires registering a credit card on your Cloudflare account. While egress bandwidth is free, exceeding storage (10 GB) or operation limits triggers automatic post-paid charges on your card.</p>
<p>In contrast, <strong>ImageKit provides a genuinely free tier without requiring a credit card</strong>. It offers <strong>3 GB of media storage and 20 GB of free global CDN bandwidth each month (as of September 2026)</strong>. Crucially, if you reach the limit, your account is temporarily paused rather than automatically billed, making it completely risk-free from unintended charges. <em>(Note: Free-tier policies may change over time depending on the provider)</em></p>
<p>For this reason, <strong>my personal recommendation is to begin with ImageKit without any credit card commitment, and then migrate to Cloudflare R2 once your blog traffic outgrows the free tier (3 GB storage or 20 GB monthly bandwidth)</strong>. The post image URL migration tool we implemented in this release was built precisely to make this transition seamless—allowing you to update all legacy image links across existing posts with a single click.</p>
<p>Regardless of the backend chosen, early implementations suffered from a major bottleneck: serving images through an internal proxy endpoint (<code>/images/...</code>). Whenever a visitor opened an article with multiple images, <strong>each image triggered a Cloudflare Worker invocation, rapidly burning through the daily free limit of 100,000 Worker requests</strong>.</p>
<p>To fix this, we introduced direct serving modes (ImageKit CDN, R2 custom domains, Supabase public URLs) so media traffic bypasses Workers entirely. Yet, a practical hurdle persisted: <strong>existing posts authored prior to the change still had legacy proxy paths (<code>&lt;p&gt;&lt;img src=&quot;/images/...&quot;&gt;&lt;/p&gt;</code>) hardcoded in their HTML body</strong>. Readers browsing archived articles continued to trigger wasteful Worker invocations.</p>
<p>Furthermore, switching storage providers or restoring backups frequently resulted in broken links or lingering proxy paths.</p>
<blockquote>
<p>⚠️ <strong>Critical Constraint: Cloudflare KV Cannot Be Directly Re-linked</strong><br>Cloudflare KV does not provide public direct URLs. Consequently, <strong>if your blog relies on default KV storage, you cannot transition to direct serving or rewrite body URLs; KV operates exclusively via the domain proxy path (<code>/images/...</code>)</strong>.<br>To slash Workers usage with this synchronization tool, you must migrate your media backend to an object storage provider such as ImageKit (recommended), Cloudflare R2, or Supabase.</p>
</blockquote>
<h3 id="2-implementation-batch-url-migration-pipeline">2) Implementation: Batch URL Migration Pipeline</h3>
<p>To ensure legacy content routes directly through the optimal CDN or storage endpoint, we built a dedicated migration pipeline:</p>
<ul>
<li><p><strong>Migration API Endpoint</strong> (<code>apps/admin/src/routes/api/media/migrate-urls/+server.ts</code>):<br>Iterates through all published posts, detects proxy paths (<code>/images/...</code>) or previous storage URL patterns using regex, and batch-replaces them with the active storage&#39;s direct endpoint (e.g., R2 custom domain, Supabase direct URL).</p>
</li>
<li><p><strong>Admin Dashboard UI</strong> (<code>apps/admin/src/routes/media/+page.svelte</code>):<br>Placed a dedicated &quot;Post Image URL Migration Tool&quot; beneath the Storage Settings tab, allowing administrators to update all legacy image links across the entire database with a single button click after changing storage modes.</p>
</li>
<li><p><strong>Backup &amp; Restore Integration</strong>:<br>Added an &quot;Auto-migrate post image URLs&quot; toggle to the backup restoration modal, ensuring restored databases immediately adapt to the active environment&#39;s direct endpoints.</p>
</li>
<li><p><strong>Outcome</strong>: Image requests on archived articles no longer invoke Cloudflare Workers. Visitors stream images directly from the storage CDN, safeguarding daily Worker quotas.</p>
</li>
</ul>
<h3 id="3-best-practices-for-safe-migration">3) ⚠️ Best Practices for Safe Migration</h3>
<p>Because batch-rewriting image URLs modifies raw post content directly inside the database, edge cases can theoretically arise across different environments. We strongly recommend following these guidelines:</p>
<ol>
<li><strong>Mandatory Full Backup Before Migration</strong>:<br>Before running the migration, use the Admin Backup feature to <strong>download a complete local backup of both your D1 database and media files</strong>.</li>
<li><strong>Prepare for Immediate Rollback</strong>:<br>Do not delete existing images from your old storage server beforehand. <strong>Keep the old storage data intact</strong> so you can instantly revert if a URL pattern maps incorrectly.</li>
<li><strong>Validate on a Staging/Clone Blog (Strongly Recommended)</strong>:<br>The safest approach is to <strong>spin up a temporary staging blog using your downloaded backup and test the migration there first</strong>. Once you confirm that all post images render flawlessly without broken links, apply the migration to your production blog.</li>
</ol>
<hr>
<h2 id="4-semantic-html-refactoring-in-layoutrenderer">4. Semantic HTML Refactoring in LayoutRenderer</h2>
<p>Previously, <code>LayoutRenderer.svelte</code> structured columns and widgets almost entirely with generic <code>div</code> tags. While visual rendering was unaffected, we refactored the component to use HTML5 semantic tags so search engine crawlers can clearly differentiate core content from peripheral widgets:</p>
<ul>
<li>Columns that do not contain the primary content widget (<code>post_content</code>) are now wrapped in <code>&lt;aside class=&quot;layout-column sidebar-column&quot;&gt;</code> to explicitly signal secondary content.</li>
<li>The main content area is cleanly separated as <code>&lt;div class=&quot;layout-column main-column&quot;&gt;</code>.</li>
<li>Individual widget blocks now use <code>&lt;section class=&quot;widget-item ...&quot;&gt;</code> accompanied by accessible <code>&lt;h3&gt;</code> headings.</li>
</ul>
<hr>
<h2 id="5-search-engine-seo-metadata-normalization">5. Search Engine (SEO) Metadata Normalization</h2>
<p>Auditing crawl behavior on production deployments revealed several subtle specification gaps, which we addressed systematically:</p>
<ol>
<li><strong>Sitemap Root <code>&lt;lastmod&gt;</code> Tag</strong> (<code>sitemap.xml/+server.ts</code>):<br>Individual post URLs included modification dates, but the root (<code>/</code>) entry lacked a <code>&lt;lastmod&gt;</code> tag. We updated the generator to reflect the timestamp of the latest published post.</li>
<li><strong>Sidebar Tag Link Encoding</strong> (<code>TagCloudWidget.svelte</code>):<br>Tags containing whitespace were previously rendered with raw query strings. We applied <code>encodeURIComponent</code> to ensure standard-compliant URLs.</li>
<li><strong>Homepage <code>og:image</code> Fallback</strong> (<code>[[lang=lang]]/+page.server.ts</code>):<br>When no custom logo was configured in the admin panel, homepage social shares lacked thumbnail previews. We implemented a fallback that automatically adopts the latest post&#39;s primary image (<code>lcpImage</code>).</li>
<li><strong><code>og:locale</code> Standardization</strong> (<code>SeoHead.svelte</code>):<br>Resolved an issue where language codes were output as raw two-letter codes (<code>en</code>, <code>ja</code>). They are now mapped to standardized Open Graph locales (<code>en_US</code>, <code>ja_JP</code>).</li>
</ol>
<hr>
<h2 id="6-admin-control-for-tag-noindex-and-i18n-dictionary-registration">6. Admin Control for Tag noindex and i18n Dictionary Registration</h2>
<h3 id="1-context">1) Context</h3>
<p>During the initial stages of a blog, having few posts often causes multiple tag pages to display identical or near-identical listings. To protect new blogs from search engine duplicate content penalties, it is best practice to serve tag archives with a <code>noindex</code> directive by default.</p>
<p>However, as a publication matures and builds depth across categories, administrators may want these tag pages indexed. Previously, enabling indexation required editing code and redeploying the entire site.</p>
<h3 id="2-implementation">2) Implementation</h3>
<ul>
<li><strong>Settings Toggle in Admin Panel</strong> (<code>apps/admin/src/routes/settings/+page.svelte</code>):<br>Added a toggle titled &quot;Block Search Engine Indexing on Tag Pages (noindex)&quot;. Turning it ON sets <code>noindex</code>, while turning it OFF permits indexing. The default setting is ON.</li>
<li><strong>Real-time Engine Integration</strong> (<code>apps/blog/src/routes/[[lang=lang]]/tags/[tag]/+page.server.ts</code>):<br>Replaced the hardcoded <code>noindex: true</code> flag with the dynamic admin setting (<code>settings?.tag_page_noindex !== &#39;false&#39;</code>), applying changes instantly without redeployment.</li>
<li><strong>i18n Dictionary Registration</strong>:<br>Registered localized labels, descriptions, and tooltips across English, Korean, and Japanese within <code>packages/shared/src/i18n/index.ts</code>.</li>
</ul>
<hr>
<h2 id="7-conclusion">7. Conclusion</h2>
<p>Rather than focusing on flashy surface-level features, the v1.0.0.15 release addresses the subtle inefficiencies and operational overhead encountered during actual, day-to-day blog management.</p>
<p>By eliminating redundant database round-trips, streamlining media storage transitions, and tightening SEO specifications, running a production blog on Cloudflare&#39;s free tier becomes significantly more resilient. We will continue refining the engine with this practical, operations-first approach.</p>
]]></content:encoded>
            <category>DevLog</category>
        </item>
        <item>
            <title><![CDATA[Engineering SKBE: Multi-Slot Design, D1 Cache Optimization, and SEO Normalization (v1.0.0.11 ~ v1.0.0.13)]]></title>
            <link>https://testblog-6br.pages.dev/en/devlog/skbe-engine-optimization-v11-v13-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/devlog/skbe-engine-optimization-v11-v13-guide</guid>
            <pubDate>Tue, 08 Sep 2026 02:58:10 GMT</pubDate>
            <description><![CDATA[A comprehensive technical retrospective on optimizations across SKBE v1.0.0.11 to v1.0.0.13: a 3-slot theme architecture, a 98% D1 read-reduction cache, mobile CLS 0.000 defense, canonical SEO normalization, and dynamic llms.txt integration.]]></description>
            <content:encoded><![CDATA[<h2 id="1-introduction-production-bottlenecks-in-practice">1. Introduction: Production Bottlenecks in Practice</h2>
<p>Following the release of SvelteKit Blog Engine (SKBE) v1.0.0.10, running the system under real-world production conditions exposed several architectural bottlenecks that weren&#39;t obvious during initial local development.</p>
<p>SKBE is designed around a <strong>$0/month serverless architecture hosted entirely on Cloudflare&#39;s Free Tier (Pages, D1 SQLite, KV)</strong>. As traffic grew and content accumulated, our primary engineering challenge was clear: how to keep the platform responsive, scalable, and stable without exceeding free-tier resource boundaries.</p>
<p>Specifically, we needed answers to three core questions:</p>
<ol>
<li><strong>D1 Query Efficiency</strong>: How do we safeguard Cloudflare D1&#39;s daily free write limit (100,000 rows/day) and drastically cut redundant read operations?</li>
<li><strong>Mobile Rendering Stability</strong>: How do we eliminate Cumulative Layout Shift (CLS) on mobile devices caused by client-side JS layout decisions?</li>
<li><strong>SEO &amp; Next-Gen AI Standards</strong>: How do we resolve subtle canonical URL contradictions in multilingual routing, and how can we support AI crawlers like ChatGPT Search and Perplexity with modern standards?</li>
</ol>
<p>Between v1.0.0.11 and v1.0.0.13, we addressed these challenges through a targeted 3-stage optimization roadmap. Here is a technical breakdown of what we discovered and how we solved it.</p>
<hr>
<h2 id="2-v10011-stability-defense-adsense-blank-renders-amp-runtime-faults">2. [v1.0.0.11] Stability Defense: AdSense Blank Renders &amp; Runtime Faults</h2>
<p>Our first focus was eliminating monetization rendering flaws and addressing edge-case runtime crashes.</p>
<h3 id="defending-against-adsense-blank-renders-on-desktop">Defending Against AdSense Blank Renders on Desktop</h3>
<p>While responsive Google AdSense units rendered reliably on mobile devices, they frequently collapsed to <code>height: 0px</code> on specific desktop viewport widths, leaving blank spaces in the sidebar and content flow.</p>
<ul>
<li><strong>Root Cause</strong>: A race condition existed between CSS Grid track calculations and the moment Google&#39;s <code>adsbygoogle.push</code> script evaluated the container&#39;s rendered width.</li>
<li><strong>Fix</strong>: We enforced an explicit <code>min-height: 280px</code> alongside responsive container queries on the ad wrapper. This ensures a stable layout bounding box is always reserved before the ad script executes, eliminating blank layout collapse.</li>
</ul>
<h3 id="hardening-tag-pages-amp-multilingual-routing">Hardening Tag Pages &amp; Multilingual Routing</h3>
<p>We resolved intermittent HTTP 500 crashes occurring when visitors queried tags with special characters or whitespace. The root issue stemmed from encoding mismatches between SvelteKit&#39;s route matchers and Drizzle ORM query bindings against D1, which we standardized using normalized URI component decoding.</p>
<hr>
<h2 id="3-v10012-architectural-leap-design-flexibility-amp-d1-smart-caching">3. [v1.0.0.12] Architectural Leap: Design Flexibility &amp; D1 Smart Caching</h2>
<p>In v1.0.0.12, we overhauled the core architecture to maximize theme flexibility while significantly reducing Cloudflare D1 read and write overhead.</p>
<h3 id="1-3-slot-multi-design-system-multi-slot-design-architecture">1) 3-Slot Multi-Design System (Multi-Slot Design Architecture)</h3>
<p>In traditional blogging engines (WordPress, Ghost, Tistory, etc.), redesigning a live site carries considerable friction. Activating a new theme overwrites the active configuration, making rollback tedious without manual backups. More critically, <strong>experimenting with a redesign in a production environment without exposing broken layouts to live visitors is virtually impossible</strong>.</p>
<p>SKBE v1.0.0.12 solves this fundamentally with a <strong>snapshot-based 3-slot theme architecture</strong>:</p>
<ul>
<li><p><strong>Concurrent Maintenance of 3 Independent Design Snapshots</strong>:</p>
<ul>
<li>The Admin Design Editor enables administrators to configure and maintain <strong>Slot 1 (Default Modern), Slot 2 (Minimalist Redesign), and Slot 3 (Dark/Event Theme)</strong> as completely independent snapshots.</li>
<li>Designing a new theme in Slot 2 has zero impact on the live Slot 1 theme. Once verified, shifting the live presentation or rolling back takes a single click.</li>
</ul>
</li>
<li><p><strong>Three Granular Delivery Strategies</strong>:</p>
<ul>
<li><strong>Fixed Single Slot</strong>: The administrator designates a single slot to serve all incoming traffic.</li>
<li><strong>Session-Based Random Rotation</strong>: Alternates slots randomly per visitor session, enabling seamless design A/B testing and a fresh visitor experience.</li>
<li><strong>Visitor Floating Theme Switcher</strong>: Renders a non-intrusive floating button that lets visitors freely toggle between available themes on the fly.</li>
</ul>
</li>
<li><p><strong>Zero-Waste Conditional SSR Payload Bundling</strong>:</p>
<ul>
<li>Offering three themes must not penalize page load performance.</li>
<li>When the visitor theme selector is disabled, <code>+layout.server.ts</code> packages <strong>only the active slot&#39;s configuration and CSS into the HTML payload</strong>, leaving secondary slots completely unbundled.</li>
<li>Secondary slots are conditionally bundled only when visitor switching is explicitly enabled. This preserves ultra-lean SSR HTML transfer sizes identical to a single-theme setup.<br><img src="https://sveltekitblog.com/images/posts/skbe-engine-optimization-v11-v13-guide/desktop/img-devlog-skbe-engine-optimization-v11-v13-guide-en-001.webp" alt="img-devlog-skbe-engine-optimization-v11-v13-guide-en-001"></li>
</ul>
</li>
</ul>
<h3 id="2-in-memory-ttl-cache-layer-amp-batch-view-buffering">2) In-Memory TTL Cache Layer &amp; Batch View Buffering</h3>
<p>To maximize Cloudflare D1&#39;s free tier, we implemented in-memory caching and batching directly within Cloudflare Workers:</p>
<ul>
<li><strong>Worker In-Memory TTL Cache (<code>cache.ts</code>)</strong>: For data that changes infrequently (site settings, active layouts, tag clouds), we introduced an in-memory cache with a 60-second TTL. This slashed Cloudflare D1 database reads (<code>rows_read</code>) by <strong>over 98%</strong> on page loads.</li>
<li><strong>Batch View Count Buffering (<code>viewBuffer.ts</code>)</strong>: Direct database write increments per page view quickly eat into D1&#39;s 100,000 daily write quota. We built an in-memory view buffer that batches updates and flushes to D1 <strong>every 10 views or 30 seconds</strong>. The client UI immediately adds pending counts in memory, ensuring visitors see real-time view counts without latency.</li>
<li><strong>Denormalized Counter Columns</strong>: Rather than computing expensive <code>COUNT(*)</code> queries on post listings, we added dedicated <code>post_count</code> columns to categories and <code>view_count</code>/<code>like_count</code> columns to posts, automated via lifecycle hooks.</li>
</ul>
<h3 id="3-mobile-core-web-vitals-cls-0000-amp-70-payload-diet">3) Mobile Core Web Vitals: CLS 0.000 &amp; 70% Payload Diet</h3>
<ul>
<li><strong>Flawless Mobile CLS Defense (0.000)</strong>: We replaced client-side JS layout calculations with a pure CSS media query architecture (<code>@media (max-width: 768px)</code>). The browser now locks the mobile 1-column layout from the very first frame (0.001s), transforming a poor <strong>CLS score of 1.0 (red) into a perfect 0.000 (green)</strong>.</li>
<li><strong>SSR HTML Diet (-70%)</strong>: We pruned over 900 administrative translation keys from the public bundle and flattened the dictionary into flat strings matching the active locale. This dropped the dictionary payload from <strong>179 KB to 9.19 KB (a 94.87% decrease)</strong>, cutting total HTML document weight by <strong>over 70%</strong>.</li>
<li><strong>Non-Blocking Web Fonts</strong>: Google Font stylesheets were converted from blocking <code>&lt;link&gt;</code> tags to asynchronous <code>rel=&quot;preload&quot;</code> + <code>onload</code> swaps, eliminating render-blocking delays and halving First Contentful Paint (FCP) times (from 4.6s down to 2.0s).</li>
</ul>
<hr>
<h2 id="4-v10013-web-standards-seo-canonical-normalization-amp-llmstxt">4. [v1.0.0.13] Web Standards: SEO Canonical Normalization &amp; llms.txt</h2>
<p>In the third stage, we normalized search engine indexing signals and introduced native support for next-generation AI crawlers.</p>
<h3 id="1-seo-canonical-url-normalization">1) SEO Canonical URL Normalization</h3>
<p>In multilingual routing (<code>[[lang=lang]]</code>), visitors browsing the default language often encountered canonical tags containing default language prefixes (e.g., <code>https://site.com/ko</code>), conflicting directly with <code>sitemap.xml</code> and <code>hreflang</code> declarations.</p>
<ul>
<li><strong>Automatic Default Prefix Stripping</strong>: Canonical tags for Home (<code>/</code>), Categories (<code>/tech</code>), and CMS pages now automatically strip the default language prefix, guaranteeing exact 1:1 parity with sitemaps to prevent duplicate indexing penalties.</li>
<li><strong>Guaranteed Absolute Fallback</strong>: We patched an edge case where an unconfigured <code>siteUrl</code> setting generated invalid relative canonical links (<code>&lt;link rel=&quot;canonical&quot; href=&quot;/tech/slug&quot;&gt;</code>). Canonical links now strictly fall back to <code>url.origin</code>, ensuring valid absolute URLs across all templates.</li>
</ul>
<h3 id="2-10-minute-view-count-deduplication-amp-rfc-6265-safe-keys">2) 10-Minute View Count Deduplication &amp; RFC 6265 Safe Keys</h3>
<ul>
<li><strong>Reload Abuse Defense</strong>: In addition to client-side <code>sessionStorage</code>, we introduced a 10-minute server-side cookie verification window. Duplicate views from rapid reloads or duplicate tabs are silently skipped at the database level.</li>
<li><strong>RFC 6265 Hash Keys</strong>: Non-ASCII Korean or special character slugs previously triggered Node/SvelteKit HTTP header crashes (<code>TypeError: argument name is invalid</code>). We now generate deterministic short alphanumeric hashes (<code>skbe_v_${hash}</code>), strictly complying with RFC 6265 cookie naming standards.</li>
</ul>
<h3 id="3-dynamic-multilingual-llmstxt-endpoints">3) Dynamic Multilingual <code>llms.txt</code> Endpoints</h3>
<p>To optimize indexing for AI search engines like ChatGPT Search, Perplexity, and Claude, we deployed a dynamic endpoint following the <strong><a href="https://llmstxt.org">llmstxt.org</a></strong> specification:</p>
<ul>
<li><strong>Multilingual Routing</strong>: Supports <code>/llms.txt</code> (default language), <code>/en/llms.txt</code>, and <code>/ja/llms.txt</code>.</li>
<li><strong>Structured Markdown Output</strong>: Dynamically renders the site summary, published categories, the 30 latest posts with excerpts, custom pages, and direct RSS/Sitemap feeds.</li>
<li><strong>Edge Caching</strong>: Configured a 10-minute Cloudflare CDN cache (<code>s-maxage=600</code>), resolving Google PageSpeed Insights mobile timeout warnings (<code>Fetch of llms.txt timed out</code>).</li>
</ul>
<hr>
<h2 id="5-performance-amp-quality-metrics-summary">5. Performance &amp; Quality Metrics Summary</h2>
<p>A consolidated overview of engineering improvements achieved across the three releases:</p>
<table>
<thead>
<tr>
<th align="left">Metric / Area</th>
<th align="left">Before Optimization</th>
<th align="left">After Optimization</th>
<th align="left">Impact</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>D1 Database Reads</strong></td>
<td align="left">Uncached read on every load</td>
<td align="left">60s Worker in-memory TTL cache</td>
<td align="left"><strong>&gt; 98% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>D1 Database Writes</strong></td>
<td align="left">Immediate write per view</td>
<td align="left">10 views / 30s batch buffer</td>
<td align="left"><strong>Protects daily free write quota</strong></td>
</tr>
<tr>
<td align="left"><strong>Mobile CLS</strong></td>
<td align="left">1.0 (JS post-mount shift)</td>
<td align="left"><strong>0.000 (Pure CSS media queries)</strong></td>
<td align="left"><strong>Zero layout shift</strong></td>
</tr>
<tr>
<td align="left"><strong>Translation Payload</strong></td>
<td align="left">179 KB (all locale keys)</td>
<td align="left"><strong>9.19 KB (single-locale flattened)</strong></td>
<td align="left"><strong>94.87% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>Total SSR HTML Size</strong></td>
<td align="left">~80 - 100 KB</td>
<td align="left"><strong>~20 - 25 KB</strong></td>
<td align="left"><strong>&gt; 70% reduction</strong></td>
</tr>
<tr>
<td align="left"><strong>FCP (First Paint)</strong></td>
<td align="left">4.6s</td>
<td align="left"><strong>2.0s</strong></td>
<td align="left"><strong>&gt; 50% faster</strong></td>
</tr>
<tr>
<td align="left"><strong>SEO Canonical</strong></td>
<td align="left">Default language prefix mismatch</td>
<td align="left">100% matched with sitemap &amp; hreflang</td>
<td align="left"><strong>Prevents duplicate indexation</strong></td>
</tr>
<tr>
<td align="left"><strong>AI Search Support</strong></td>
<td align="left">None (Lighthouse warning)</td>
<td align="left"><strong>Full llms.txt standard support</strong></td>
<td align="left"><strong>Ready for AI crawler citation</strong></td>
</tr>
</tbody></table>
<hr>
<h2 id="6-closing-behind-the-scenes-amp-hub-notice">6. Closing: Behind the Scenes &amp; Hub Notice</h2>
<h3 id="why-we-consolidated-v10011-through-v10013">Why We Consolidated v1.0.0.11 Through v1.0.0.13</h3>
<p>To be transparent: v1.0.0.11 contained minor bug fixes that felt too lightweight for a standalone article, so our original plan was to combine it with the major v1.0.0.12 release.</p>
<p>However, immediately following v1.0.0.12, we identified critical defects in non-ASCII slug view counting and subtle canonical URL mismatches (v1.0.0.13). For developers and cloners actively using this engine, <strong>fixing runtime crashes and deploying production patches to GitHub took absolute priority over writing blog posts</strong>.</p>
<p>After stabilizing the codebase and shipping the hotfixes, we sat down to compile the full technical context—which naturally evolved into this comprehensive three-version retrospective.</p>
<h3 id="temporary-hub-maintenance-notice">Temporary Hub Maintenance Notice</h3>
<p>The sub-blog <strong>Hub</strong> feature is currently <strong>temporarily closed while our Google AdSense application is under review</strong>. This was done deliberately to maintain strict URL hierarchy consistency and avoid crawling noise during the inspection process.</p>
<p>Once the AdSense review concludes, we will perform a routine architecture check and re-open the Hub immediately. We appreciate your patience while we finalize this step.</p>
<hr>
<p>Across v1.0.0.11, v1.0.0.12, and v1.0.0.13, our focus wasn&#39;t adding arbitrary complexity, but <strong>pushing real-world performance, stability, and web standards to their limits within the boundaries of Cloudflare&#39;s Free Tier</strong>.</p>
<p>The complete, open-source codebase is available on our official GitHub repository. We hope these architectural patterns offer practical insights for anyone building full-stack applications with SvelteKit and Cloudflare Workers!</p>
]]></content:encoded>
            <category>DevLog</category>
            <enclosure url="https://sveltekitblog.com/images/posts/skbe-engine-optimization-v11-v13-guide/desktop/img-devlog-skbe-engine-optimization-v11-v13-guide-en-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[SKBE v1.0.0.10 Update: Multilingual Post Tabs, Hub Sync Polish, and Editor Safeguards]]></title>
            <link>https://testblog-6br.pages.dev/en/devlog/release-v1-0-0-10-post-management-tabs-and-hub-sync</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/devlog/release-v1-0-0-10-post-management-tabs-and-hub-sync</guid>
            <pubDate>Wed, 26 Aug 2026 06:12:17 GMT</pubDate>
            <description><![CDATA[Introducing v1.0.0.10: Dynamic language filter tabs for multilingual posts, refined Blog Hub federation protocols, editor data loss protection guards, and simplified deployment CLI.]]></description>
            <content:encoded><![CDATA[<h1 id="sveltekit-blog-engine-v10010-release-notes">SvelteKit Blog Engine v1.0.0.10 Release Notes</h1>
<p>Here is a summary of the updates introduced in <strong>SvelteKit Blog Engine v1.0.0.10</strong>.</p>
<p>As multilingual publishing grew, managing mixed-language articles in a single chronological list became increasingly tedious. In this update, we focused on implementing <strong>dynamic language filter tabs</strong> in the admin post management dashboard, <strong>refining the Blog Hub federation protocol</strong>, <strong>adding editor safeguards against formatting loss</strong>, and <strong>simplifying multi-account deployment commands</strong>.</p>
<hr>
<h2 id="1-dynamic-multilingual-filter-tabs-in-post-management">1. Dynamic Multilingual Filter Tabs in Post Management</h2>
<p>Previously, all posts were listed strictly in chronological order regardless of their language. Managing dozens of articles across Korean, English, and Japanese required unnecessary scrolling and searching.</p>
<p><img src="https://sveltekitblog.com/images/posts/release-v1-0-0-10-post-management-tabs-and-hub-sync/desktop/img-devlog-release-v1-0-0-10-post-management-tabs-and-hub-sync-en-002.webp" alt="img-devlog-release-v1-0-0-10-post-management-tabs-and-hub-sync-en-002"></p>
<h3 id="key-improvements">Key Improvements</h3>
<ul>
<li><strong>Dynamic Language Detection &amp; Live Count Badges</strong>: Instead of hardcoding language codes, the system queries active database languages and post entries in real time. Each tab displays live post counts (e.g., <code>[All (24)]</code>, <code>[Korean (10)]</code>, <code>[English (8)]</code>, <code>[Japanese (6)]</code>).</li>
<li><strong>Smart Empty States</strong>: Clicking a language tab with zero posts (e.g., <code>English (0)</code>) shows a helpful prompt along with a direct <code>[+ Write New Post in this language]</code> button to streamline the authoring workflow.</li>
<li><strong>Single-Language Mode Support</strong>: For blogs operating in only one language, the tab bar remains hidden to keep the interface clean and minimal.</li>
<li><strong>Language Chips &amp; Safe Pagination</strong>: Table rows now display crisp language badges (<code>KR</code>, <code>EN</code>, <code>JA</code>, etc.) next to titles, and switching tabs automatically resets the view to Page 1 to avoid blank pagination bugs.</li>
</ul>
<hr>
<h2 id="2-refined-blog-hub-hubsveltekitblogcom-integration">2. Refined Blog Hub (<code>hub.sveltekitblog.com</code>) Integration</h2>
<p>We polished several edge cases when syncing articles to the centralized feed platform, SvelteKit Blog Hub.</p>
<h3 id="improvements">Improvements</h3>
<ul>
<li><strong>1,500-Character Standard HTML Intro Extraction</strong>: Automatically parses and extracts clean 1,000–1,500 character HTML introductory excerpts from both Markdown and Visual HTML formats for feed summary cards.</li>
<li><strong>Absolute Image URL Conversion</strong>: Transforms relative image paths (<code>/images/posts/...</code>) into absolute URLs containing the canonical domain (<code>https://yourdomain.com/images/...</code>) so that thumbnails and in-article images render properly on external feeds.</li>
<li><strong>Publish Date Timeline Preservation</strong>: When updating an existing article, the original <code>published_at</code> timestamp is preserved instead of being overwritten by the edit date, keeping the Hub timeline chronological.</li>
<li><strong>Default Auto-Publish Setting</strong>: Added a toggle under Admin Settings (<code>⚙️ Settings</code>) to enable &quot;Auto-Submit to Hub&quot; by default for new posts.</li>
</ul>
<hr>
<h2 id="3-post-editor-data-loss-protection-guards">3. Post Editor Data Loss Protection Guards</h2>
<p>We added protective measures in the post editor to prevent accidental loss of content or formatting when switching modes.</p>
<ul>
<li><strong>HTML to Visual Mode Warning</strong>: Switching from raw HTML to the Visual editor can cause non-standard tags, custom tables, or inline styles to be sanitized by the editor parser. A confirmation dialog now alerts you before the switch occurs.</li>
<li><strong>Editor Format Lock</strong>: Existing saved posts have their editor type (HTML vs. Markdown) locked to prevent accidental corruption, with warning prompts if changes are attempted.</li>
</ul>
<hr>
<h2 id="4-simplified-multi-account-deployment-cli">4. Simplified Multi-Account Deployment CLI</h2>
<p>The <code>deploy-multi</code> script arguments have been streamlined for faster and more intuitive operations:</p>
<pre><code class="language-bash"># Previous format
npm run deploy:multi -- myaccount --admin-only

# New streamlined format
npm run deploy:multi -- myaccount admin   # Fast admin-only deploy
npm run deploy:multi -- myaccount blog    # Fast blog-only deploy
npm run deploy:multi -- myaccount         # Deploy both blog and admin
</code></pre>
<p><em>(Legacy flags like <code>--admin-only</code> and <code>--blog-only</code> remain 100% backward compatible.)</em></p>
<hr>
<h2 id="how-to-apply-the-update">How to Apply the Update</h2>
<p>Pull the latest repository commits and run your deployment command:</p>
<pre><code class="language-bash"># Pull latest code
git pull origin main

# Deploy admin (example)
npm run deploy:admin
# Or for multi-account deployment
npm run deploy:multi -- &lt;account_name&gt; admin
</code></pre>
<p>We will continue to refine and enhance the engine based on real-world usage. Thank you!</p>
]]></content:encoded>
            <category>DevLog</category>
            <enclosure url="https://sveltekitblog.com/images/posts/release-v1-0-0-10-post-management-tabs-and-hub-sync/desktop/img-devlog-release-v1-0-0-10-post-management-tabs-and-hub-sync-en-002.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Comprehensive Guide to SvelteKitBlog Hub Syndication and Auto-Publishing]]></title>
            <link>https://testblog-6br.pages.dev/en/detail-manual/blog-hub-integration-manual</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/detail-manual/blog-hub-integration-manual</guid>
            <pubDate>Tue, 25 Aug 2026 22:28:23 GMT</pubDate>
            <description><![CDATA[A comprehensive guide to federating your standalone blog with SvelteKitBlog Hub (hub.sveltekitblog.com) to expand traffic and safely automate the post creation, update, and deletion lifecycle.]]></description>
            <content:encoded><![CDATA[<h1 id="comprehensive-guide-to-sveltekitblog-hub-syndication-and-auto-publishing">🌐 Comprehensive Guide to SvelteKitBlog Hub Syndication and Auto-Publishing</h1>
<p>When operating a standalone personal blog, the biggest challenge is <strong>&quot;How can I introduce my articles to new readers?&quot;</strong> Unlike commercial blogging platforms, a self-hosted blog gives you complete freedom over your data and design, but attracting initial traffic and achieving search engine visibility requires considerable time and effort.</p>
<p><strong><a href="https://hub.sveltekitblog.com/">SvelteKit Blog Hub (hub.sveltekitblog.com)</a></strong> is a decentralized federated feed platform designed to overcome the discoverability limitations of independent blogs, allowing creators to share readership, statistics, and mutual growth.</p>
<p>This guide provides a detailed walkthrough of everything you need to know—from <strong>obtaining an API Key and configuring the admin settings to managing the post lifecycle and leveraging Google AdSense synergies</strong>.</p>
<hr>
<h2 id="1-key-features-and-working-principles">1. 🎯 Key Features and Working Principles</h2>
<p><img src="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-en-001.webp" alt="img-detail-manual-blog-hub-integration-manual-en-001"></p>
<ol>
<li><strong>Complete Data Independence &amp; Ownership</strong>:<ul>
<li>The full article text and all database entries remain securely within your own Cloudflare D1 instance.</li>
<li>Only a <strong>standard HTML introduction (1,000–1,500 characters)</strong> and essential metadata (title, thumbnail, slug, etc.) are transmitted to the hub for feed card rendering.</li>
</ul>
</li>
<li><strong>Automatic Absolute URL Conversion for Images</strong>:<ul>
<li>Relative image paths (<code>/images/...</code>) embedded in Markdown or HTML are automatically transformed into absolute URLs (<code>https://yourdomain.com/...</code>), ensuring crystal-clear previews on the hub feed without broken links.</li>
</ul>
</li>
<li><strong>Non-blocking &amp; Fault-Tolerant Isolation</strong>:<ul>
<li>Even if the hub server experiences network latency or temporary downtime, <strong>your blog&#39;s internal post-saving transaction is guaranteed to complete normally without any interruption</strong>.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="2-step-1-obtain-a-hub-api-key-amp-configure-settings">2. 🔑 Step 1: Obtain a Hub API Key &amp; Configure Settings</h2>
<h3 id="obtain-an-api-key-from-the-hub-platform">① Obtain an API Key from the Hub Platform</h3>
<ol>
<li>Visit <strong><a href="https://hub.sveltekitblog.com/register-site">SvelteKit Blog Hub &#39;Connect Blog (Get API Key)&#39;</a></strong>.<br><img src="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-en-002.webp?v=2" alt="img-detail-manual-blog-hub-integration-manual-en-002"></li>
<li>Register your blog&#39;s canonical URL (e.g., <code>https://myblog.com</code>) and copy the uniquely generated <strong>Site API Key</strong> (<code>sk_board_...</code> or <code>sk_hub_...</code>).</li>
</ol>
<h3 id="configure-blog-admin-settings-settings">② Configure Blog Admin Settings (<code>⚙️ Settings</code>)</h3>
<ol>
<li>Log in to your blog&#39;s admin dashboard and navigate to <strong>[Settings]</strong>.</li>
<li>Locate the <strong>SvelteKitBlog Hub Syndication Settings</strong> section.<br><img src="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-en-003.webp?v=2" alt="img-detail-manual-blog-hub-integration-manual-en-003"></li>
</ol>
<pre><code class="language-text">[ SvelteKit Blog Hub Integration Settings ]
* Hub Platform URL : https://hub.sveltekitblog.com (Fixed)
* Hub Issued API Key : sk_hub_live_... (Enter your issued key)
* 🌐 Enable Auto-Publish to Hub by Default : [ON / OFF Toggle]
</code></pre>
<ol start="3">
<li>Enabling <code>Auto-Syndication by Default</code> will pre-check the syndication option whenever you create a new post.</li>
<li>Click <strong>[Save Settings]</strong> at the bottom to apply the configuration.</li>
</ol>
<hr>
<h2 id="3-step-2-auto-publishing-from-the-post-editor">3. ✍️ Step 2: Auto-Publishing from the Post Editor</h2>
<p>At the bottom of the metadata sidebar in the post editor (<code>New Post</code> or <code>Edit Post</code>), you will find the <strong>Hub Auto-Submission checkbox</strong>.<br><img src="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-en-004.webp" alt="img-detail-manual-blog-hub-integration-manual-en-004"></p>
<pre><code class="language-text">[☑️] 🌐 Automatically submit to SvelteKit Blog Hub (hub.sveltekitblog.com)
     When checked, a summary card is automatically submitted to the Hub feed upon publishing.
     When unchecked, it is set to &quot;Hidden&quot; on the Hub to safely preserve like statistics;
     to delete completely from the Hub, you must delete the post on your blog.
</code></pre>
<ul>
<li><strong>Publishing Public Posts (<code>Status: Published</code>)</strong>:<ul>
<li>When saved with the checkbox active, the HTML intro and thumbnail are dispatched to the hub simultaneously with the D1 database write, appearing instantly on the hub feed.</li>
</ul>
</li>
<li><strong>Saving Drafts (<code>Status: Draft</code>)</strong>:<ul>
<li>Posts saved as drafts will never be exposed on the public hub feed, even if the checkbox is checked.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="4-step-3-lifecycle-management-edit-url-change-hide-delete">4. 🔄 Step 3: Lifecycle Management (Edit, URL Change, Hide, Delete)</h2>
<p>As you maintain your blog, you may update article content, reclassify categories, or hide/delete posts. The blog engine automatically detects these lifecycle events and syncs with the hub flawlessly.</p>
<table>
<thead>
<tr>
<th align="left">Scenario</th>
<th align="left">Blog Action</th>
<th align="left">Hub Synchronization Behavior</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Content Update</strong></td>
<td align="left">Edit body and save</td>
<td align="left"><strong>Preserves the original publication date (<code>published_at</code>)</strong> while updating card content and thumbnail (avoids jumping to the top of the feed unnecessarily)</td>
</tr>
<tr>
<td align="left"><strong>Slug / Category Change</strong></td>
<td align="left">Change URL and save</td>
<td align="left">Automatically <strong>deletes the previous URL card (<code>DELETE</code>)</strong> and <strong>registers the new URL (<code>POST</code>)</strong> (prevents broken 404 links)</td>
</tr>
<tr>
<td align="left"><strong>Unpublish / Uncheck</strong></td>
<td align="left">Switch to Draft or uncheck</td>
<td align="left">Sets card status to <strong><code>hidden</code></strong> (reader <strong>upvote/like statistics are permanently preserved</strong>)</td>
</tr>
<tr>
<td align="left"><strong>Delete Post</strong></td>
<td align="left">Delete post from admin</td>
<td align="left">Completely <strong>removes the card from the hub (<code>DELETE</code>)</strong></td>
</tr>
</tbody></table>
<hr>
<h2 id="5-google-adsense-readiness-amp-synergies">5. 💰 Google AdSense Readiness &amp; Synergies</h2>
<p>Traffic driven from the hub significantly accelerates Google AdSense approval and ad revenue generation.</p>
<ol>
<li><strong>Securing Initial Active Traffic</strong>:<ul>
<li>Google crawlers index and rate websites with active, genuine reader traffic much faster than dormant sites with zero visitors.</li>
</ul>
</li>
<li><strong>Automated <code>ads.txt</code> Serving</strong>:<ul>
<li>Entering your AdSense authorization snippet (<code>google.com, pub-..., DIRECT, f08c47fec0942fa0</code>) in the admin settings serves it immediately at <code>https://yourdomain.com/ads.txt</code>.</li>
</ul>
</li>
<li><strong>Content Quality Guidelines</strong>:<ul>
<li>For primary AdSense approval, we recommend consistently publishing <strong>15–20 high-quality technical or informative articles (1,500+ characters each)</strong> syndicated to the hub.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="6-conclusion">6. 🏁 Conclusion</h2>
<p>By integrating with SvelteKitBlog Hub, you maintain 100% data sovereignty over your standalone blog while unlocking the powerful network synergy of a unified community feed!</p>
]]></content:encoded>
            <category>Detailed Manual</category>
            <enclosure url="https://sveltekitblog.com/images/posts/blog-hub-integration-manual/desktop/img-detail-manual-blog-hub-integration-manual-en-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Safely Deploying New Releases: Cloudflare Pages Multi-Account Deployment Automation]]></title>
            <link>https://testblog-6br.pages.dev/en/general-guide/cloudflare-pages-multi-account-deploy-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/general-guide/cloudflare-pages-multi-account-deploy-guide</guid>
            <pubDate>Sun, 23 Aug 2026 13:12:23 GMT</pubDate>
            <description><![CDATA[How to safely verify new engine releases on a staging test account before deploying to production! A complete guide to one-click multi-account Cloudflare Pages deployment.]]></description>
            <content:encoded><![CDATA[<h1 id="safely-deploying-new-releases-cloudflare-pages-multi-account-deployment-automation">🚀 Safely Deploying New Releases: Cloudflare Pages Multi-Account Deployment Automation</h1>
<p>When a new engine release arrives in the GitHub repository and you update your codebase, one natural question arises:</p>
<blockquote>
<p><strong>&quot;Will this new core release and database migration apply smoothly to my live production blog without breaking anything?&quot;</strong></p>
</blockquote>
<p>In SvelteKit Blog Engine, all your custom design settings (layouts, colors, fonts) and published posts are <strong>safely preserved in the D1 database</strong>, meaning updating the engine codebase will never wipe out your customized blog UI.</p>
<p>However, you may still want to verify that new backend features, D1 schema migrations, and deployment scripts run properly in your Cloudflare environment. The safest approach is to <strong>deploy to a free auxiliary Cloudflare account (or staging test instance) first, verify everything visually, and then deploy to your main production account with confidence</strong>.</p>
<p>Standard Cloudflare tools (Wrangler) traditionally required tedious browser re-logins, manual ID swapping in config files, and often suffered from local cache collisions. This guide outlines the <strong>one-click multi-account deployment pipeline</strong> that makes staged release verification seamless and risk-free.</p>
<hr>
<h2 id="1-safe-release-deployment-workflow">🔄 1. Safe Release Deployment Workflow</h2>
<p>With the multi-account deployment runner, you can establish a robust release cycle:<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-multi-account-deploy-guide/desktop/img-general-guide-cloudflare-pages-multi-account-deploy-guide-en-001.webp" alt="img-general-guide-cloudflare-pages-multi-account-deploy-guide-en-001"></p>
<hr>
<h2 id="2-prerequisites-cloudflare-api-tokens-once-per-account">🔑 2. Prerequisites: Cloudflare API Tokens (Once per Account)</h2>
<p>To deploy in the background without browser popups, create a dedicated <strong>API Token</strong> for each Cloudflare account once.</p>
<h3 id="2-1-creating-a-least-privilege-api-token">2-1. Creating a Least-Privilege API Token</h3>
<ol>
<li>Log in to the <a href="https://dash.cloudflare.com">Cloudflare Dashboard</a> ➔ Top-Right <strong>[My Profile]</strong> ➔ <strong>[API Tokens]</strong>.</li>
<li>Click <strong>[Create Token]</strong> and select <strong>[Use template]</strong> next to <strong><code>Edit Cloudflare Pages</code></strong>.</li>
<li>Under <strong>Permissions</strong>, click <strong><code>+ Add more</code></strong> to append the following two permissions:<ul>
<li><strong><code>Account</code></strong> - <strong><code>D1</code></strong> - <strong><code>Edit</code></strong> <em>(For automatic D1 database schema migrations)</em></li>
<li><strong><code>Account</code></strong> - <strong><code>Workers KV Storage</code></strong> - <strong><code>Edit</code></strong> <em>(For image storage bucket bindings)</em></li>
</ul>
</li>
</ol>
<blockquote>
<p><strong>💡 Security Tip</strong>: Instead of a Global API Key, issuing a token with <strong>only Pages, D1, and KV permissions (Principle of Least Privilege)</strong> is much safer.</p>
</blockquote>
<ol start="4">
<li>Select your target account under <strong>Account Resources</strong>, then click <strong>[Continue to summary] ➔ [Create Token]</strong>.</li>
<li>Copy the generated <strong>API Token string</strong>.</li>
</ol>
<h3 id="2-2-locating-your-32-character-account-id">2-2. Locating Your 32-Character Account ID</h3>
<p>From the browser address bar while inside your dashboard:</p>
<pre><code class="language-text">https://dash.cloudflare.com/1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d/workers-and-pages
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                             (This 32-character string is your Account ID)
</code></pre>
<hr>
<h2 id="3-instant-account-registration-via-backup-deploysync">📦 3. Instant Account Registration via Backup (<code>deploy:sync</code>)</h2>
<p>Instead of manually copying D1 UUIDs and KV IDs from the dashboard, you can register accounts instantly using the <strong>built-in backup download feature in Admin</strong>.</p>
<ol>
<li>In the target blog&#39;s Admin Dashboard ➔ <strong>[Settings] ➔ [Data Management]</strong>, click <strong>[Download Deployment Config]</strong>.</li>
<li>Rename the downloaded file to your desired account identifier and place it in the project root:<ul>
<li>Example (Test account): <code>wrangler.backup.test.json</code></li>
<li>Example (Main account): <code>wrangler.backup.main.json</code></li>
</ul>
</li>
<li>Run the sync command in your terminal:</li>
</ol>
<pre><code class="language-bash">npm run deploy:sync
</code></pre>
<pre><code class="language-text">======================================================
✅ [.deploy-accounts.json] Account Sync Completed!
======================================================
📋 [Registered Accounts]
  - main (Main Blog Account): 🟢 Ready
  - test (Test Blog Account): 🟡 token/accountId required
</code></pre>
<ol start="4">
<li>Open <code>.deploy-accounts.json</code> and fill in your <code>token</code> and <code>accountId</code>:</li>
</ol>
<pre><code class="language-json">{
  &quot;test&quot;: {
    &quot;name&quot;: &quot;Test Staging Account&quot;,
    &quot;token&quot;: &quot;YOUR_ACTUAL_API_TOKEN&quot;,
    &quot;accountId&quot;: &quot;YOUR_32_CHAR_ACCOUNT_ID&quot;,
    &quot;blogProject&quot;: &quot;test-blog-web&quot;,
    &quot;adminProject&quot;: &quot;test-blog-admin&quot;,
    &quot;d1&quot;: {
      &quot;BLOG_DB&quot;: { &quot;name&quot;: &quot;test-blog-db&quot;, &quot;id&quot;: &quot;11111111-2222-3333-4444-555555555555&quot; },
      &quot;USER_DB&quot;: { &quot;name&quot;: &quot;test-user-db&quot;, &quot;id&quot;: &quot;66666666-7777-8888-9999-000000000000&quot; }
    },
    &quot;kv&quot;: {
      &quot;IMAGES_KV&quot;: &quot;aaaaaaaaaabbbbbbbbbbccccccccccdd&quot;
    }
  }
}
</code></pre>
<blockquote>
<p><strong>Safe Merge</strong>: <code>deploy:sync</code> safely preserves existing tokens and account IDs without overwriting them.</p>
</blockquote>
<hr>
<h2 id="4-real-world-release-deployment-scenarios">🚀 4. Real-World Release Deployment Scenarios</h2>
<h3 id="scenario-a-test-staging-verification-first-recommended">Scenario A. Test Staging Verification First (Recommended)</h3>
<p>Deploy the new core code to your test account first to verify Blog (Web) and Admin operations:</p>
<pre><code class="language-bash">npm run deploy:multi test
</code></pre>
<h3 id="scenario-b-safe-production-deployment">Scenario B. Safe Production Deployment</h3>
<p>Once verified on staging, deploy to your main live blog:</p>
<pre><code class="language-bash">npm run deploy:multi main
</code></pre>
<h3 id="scenario-c-batch-upgrade-across-multiple-blogs">Scenario C. Batch Upgrade Across Multiple Blogs</h3>
<p>If you operate multiple sub-blogs alongside your main blog, upgrade all registered instances at once:</p>
<pre><code class="language-bash">npm run deploy:multi -- --all
</code></pre>
<h3 id="scenario-d-selective-deployment-blog-web-or-admin-only">Scenario D. Selective Deployment (Blog Web or Admin Only)</h3>
<pre><code class="language-bash"># Deploy only Blog frontend
npm run deploy:multi test blog

# Deploy only Admin dashboard
npm run deploy:multi test admin
</code></pre>
<hr>
<h2 id="5-3-stage-sandbox-isolation-architecture">🛡️ 5. 3-Stage Sandbox Isolation Architecture</h2>
<p><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-multi-account-deploy-guide/desktop/img-general-guide-cloudflare-pages-multi-account-deploy-guide-en-002.webp" alt="img-general-guide-cloudflare-pages-multi-account-deploy-guide-en-002"></p>
<ol>
<li><strong>Session Sandbox</strong>: Isolates <code>APPDATA</code> to an ephemeral sandbox folder, preserving your host machine&#39;s global login session.</li>
<li><strong>Atomic Config Swap</strong>: Swaps in target <code>wrangler.json</code> bindings only during active deployment and restores originals in the <code>finally</code> block 100% reliably.</li>
<li><strong>Automated Cache Purging</strong>: Purges local <code>.wrangler</code> caches immediately after execution, allowing seamless switching between native single-account commands (<code>deploy:blog</code>, <code>deploy:admin</code>) and multi-account runs.</li>
</ol>
<hr>
<h2 id="6-command-reference">📋 6. Command Reference</h2>
<table>
<thead>
<tr>
<th align="left">Purpose</th>
<th align="left">Command</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Auto-Sync Account Configs</strong></td>
<td align="left"><code>npm run deploy:sync</code></td>
</tr>
<tr>
<td align="left"><strong>Deploy Specific Account (Blog + Admin)</strong></td>
<td align="left"><code>npm run deploy:multi &lt;accountKey&gt;</code></td>
</tr>
<tr>
<td align="left"><strong>Deploy Specific Account Blog Only</strong></td>
<td align="left"><code>npm run deploy:multi &lt;accountKey&gt; blog</code></td>
</tr>
<tr>
<td align="left"><strong>Deploy Specific Account Admin Only</strong></td>
<td align="left"><code>npm run deploy:multi &lt;accountKey&gt; admin</code></td>
</tr>
<tr>
<td align="left"><strong>Batch Upgrade All Accounts</strong></td>
<td align="left"><code>npm run deploy:multi -- --all</code></td>
</tr>
</tbody></table>
<hr>
<h2 id="7-conclusion">💡 7. Conclusion</h2>
<p>With your custom design settings and post contents safely isolated in D1, having an automated staging-to-production deployment pipeline turns release upgrades into a worry-free, dependable experience.</p>
]]></content:encoded>
            <category>General Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/cloudflare-pages-multi-account-deploy-guide/desktop/img-general-guide-cloudflare-pages-multi-account-deploy-guide-en-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[[Devlog] SvelteKit Blog Engine v1.0.0.9 Release & Key Updates]]></title>
            <link>https://testblog-6br.pages.dev/en/devlog/devlog-release-v1009</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/devlog/devlog-release-v1009</guid>
            <pubDate>Sun, 23 Aug 2026 12:48:53 GMT</pubDate>
            <description><![CDATA[SvelteKit Blog Engine v1.0.0.9 is out! Introducing Multi-Account One-Click Cloudflare Pages Deployment, Blog Hub Integration, Full SEO Optimization for Google AdSense Approval, and i18n Polish.]]></description>
            <content:encoded><![CDATA[<h1 id="sveltekit-blog-engine-v1009-release">🚀 SvelteKit Blog Engine v1.0.0.9 Release</h1>
<p>Following the v1.0.0.8 release, v1.0.0.9 introduces <strong>Multi-Account One-Click Cloudflare Pages Smart Deployment</strong>, <strong><a href="https://hub.sveltekitblog.com/">SvelteKit Blog Hub BLOG FEED</a> Integration</strong>, <strong>Full SEO/Sitemap Optimization with Unified RSS/Sitemap Visual Styling for Google AdSense Approval</strong>, and <strong>i18n Multi-Language Dictionary Polish &amp; Deployment Sync</strong>.</p>
<hr>
<h3 id="1-multi-account-smart-auto-sync-amp-one-click-cloudflare-pages-deployment">1. 🌐 Multi-Account Smart Auto-Sync &amp; One-Click Cloudflare Pages Deployment</h3>
<ul>
<li><strong>Auto-Sync via <code>wrangler.backup.json</code> (<code>npm run deploy:sync</code>)</strong>:<ul>
<li>Simply placing downloaded backup files (<code>wrangler.backup*.json</code>) from admin into the project root and running sync automatically extracts D1 IDs, KV IDs, and project names into <code>.deploy-accounts.json</code>.</li>
<li>Existing user-defined API tokens and account IDs are 100% safely preserved (merged) without loss.</li>
</ul>
</li>
<li><strong>One-Click Multi-Account Deployment Runner (<code>npm run deploy:multi</code>)</strong>:<ul>
<li>Automatically deploys <strong>Blog + Admin (2 CFPs per account)</strong> in sequence using <code>npm run deploy:multi -- &lt;accountKey&gt;</code>.</li>
<li>Supports <code>--blog-only</code>, <code>--admin-only</code> selective flags and full batch deployment across all registered accounts (<code>--all</code>).</li>
</ul>
</li>
<li><strong>Complete Sandbox Isolation &amp; Automated Cache Purging</strong>:<ul>
<li>Protects host global Wrangler configs via isolated <code>APPDATA: .wrangler-multi-temp</code>.</li>
<li>Performs atomic in-memory <code>wrangler.json</code> swapping during deployment and restores original configs in <code>finally</code>.</li>
<li>Automatically purges local <code>.wrangler</code> and <code>node_modules/.cache/wrangler</code> caches immediately after deployment, guaranteeing zero contamination of native single-account deployments (<code>npm run deploy:admin</code>, <code>npm run deploy:blog</code>).</li>
</ul>
</li>
<li><strong>Automated D1 Schema Migration (<code>sync-secrets.js</code>)</strong>:<ul>
<li>Automatically runs safe schema migrations (such as adding the <code>is_syndicated</code> column) during deployment pipelines.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-sveltekit-blog-hub-feed-amp-real-time-post-integration">2. 📡 SvelteKit Blog Hub Feed &amp; Real-Time Post Integration</h3>
<ul>
<li><strong>Real-Time Hub Client Module (<code>hub.ts</code>, <code>syndication.ts</code>)</strong>:<ul>
<li>Establishes a real-time post transmission pipeline delivering published article metadata to <strong><a href="https://hub.sveltekitblog.com/">SvelteKit Blog Hub BLOG FEED</a></strong>.</li>
</ul>
</li>
<li><strong>Admin Editor Hub Integration Toggle (<code>PostMetadataForm.svelte</code>)</strong>:<ul>
<li>Added an intuitive UI toggle for hub feed delivery (<code>is_syndicated</code>) when creating or editing posts.</li>
</ul>
</li>
<li><strong>Database Schema Expansion (<code>schema-blog-db.sql</code>)</strong>:<ul>
<li>Formally added <code>is_syndicated INTEGER DEFAULT 0</code> column to the <code>posts</code> table.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3-google-adsense-quotlow-value-contentquot-resolution-amp-seo-optimization">3. 🔍 Google AdSense &quot;Low Value Content&quot; Resolution &amp; SEO Optimization</h3>
<ul>
<li><strong>Pure Post-Centric Search Indexing</strong>:<ul>
<li>Applied <code>noindex, follow</code> to category archive pages (<code>/[category]</code>), tag archive pages (<code>/tags/[tag]</code>), and guestbook (<code>/guestbook</code>) to eliminate thin content penalties while retaining full link traversal.</li>
<li>High-value article posts and essential legal/about CMS pages (<code>about</code>, <code>privacy</code>, <code>contact</code>) remain 100% indexed (<code>index, follow</code>) and are submitted via <code>sitemap.xml</code> with priority 0.9.</li>
</ul>
</li>
<li><strong>Sitemap &amp; RSS Query Sanitization with Unified Visual Styling (<code>sitemap.xsl</code>)</strong>:<ul>
<li>Sanitized DB queries for <code>sitemap.xml</code> and <code>rss.xml</code> to exclude CMS pages and category slugs, ensuring 100% pure published article posts are submitted.</li>
<li>Optimized published CMS page priority to 0.9 with complete multilingual alternate hreflang support.</li>
<li><strong>Introduced <code>static/sitemap.xsl</code> designed to 100% match the visual aesthetic of <code>rss.xsl</code></strong>, unifying browser rendering into clean, responsive English table layouts.</li>
<li>Adjusted <code>sitemap.xml</code> cache policy to <code>public, max-age=3600</code> for guaranteed 1-hour freshness.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="4-i18n-multi-language-dictionary-polish-amp-deployment-sync">4. 🌐 i18n Multi-Language Dictionary Polish &amp; Deployment Sync</h3>
<ul>
<li><strong>Translation Accuracy &amp; Quality Polish</strong>:<ul>
<li>Refined multilingual dictionary translations to improve UI consistency and correct translation inaccuracies (e.g., Japanese locale fixes).</li>
</ul>
</li>
<li><strong>Interactive i18n DB Sync during Admin Deployment (<code>deploy:admin</code>)</strong>:<ul>
<li>Added an interactive prompt to easily synchronize local i18n dictionary updates to the D1 database (<code>ui_dictionary</code>) during admin deployment.</li>
</ul>
</li>
<li><strong>Admin UI Localization</strong>:<ul>
<li>Localized remaining hardcoded UI elements in Hub-related dialogs and settings.</li>
</ul>
</li>
</ul>
]]></content:encoded>
            <category>DevLog</category>
        </item>
        <item>
            <title><![CDATA[[Devlog] Release History Summary (v1.0.0.4 ~ v1.0.0.8)]]></title>
            <link>https://testblog-6br.pages.dev/en/devlog/devlog-releases-v1004-to-v1008</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/devlog/devlog-releases-v1004-to-v1008</guid>
            <pubDate>Sun, 23 Aug 2026 12:38:32 GMT</pubDate>
            <description><![CDATA[SvelteKit Blog Engine Release History Summary (v1.0.0.4 ~ v1.0.0.8)]]></description>
            <content:encoded><![CDATA[<h3 id="release-v1008">🚀 Release v1.0.0.8</h3>
<ul>
<li><strong>Unified Category Widget</strong>: Completely removed the legacy <code>category_link</code> option to eliminate confusion and unified all category displays under a single <strong>Category Menu</strong> widget.</li>
<li><strong>Post Count Display Option (<code>showPostCount</code>)</strong>: Added a toggle checkbox in the admin design editor to show/hide post counts next to category names (e.g., <code>Development (5)</code>).</li>
<li><strong>Admin Design Live Preview Fix</strong>: Enhanced widget previews in the design editor so that Recent Posts, Popular Posts, and Category Menus render realistic placeholders accurately.</li>
<li><strong>Smart <code>hreflang</code> Filtering (SEO)</strong>: Generates <code>hreflang</code> alternate tags <strong>only for languages that contain published posts</strong>, effectively preventing search engines (e.g., Google AdSense / Search Console) from indexing empty fallback pages as thin content.</li>
</ul>
<hr>
<h3 id="release-v1007">🚀 Release v1.0.0.7</h3>
<ul>
<li><strong>Official Engine Version System (<code>APP_VERSION</code>)</strong>: Introduced the <code>APP_VERSION</code> constant in <code>@sveltekitblog/shared</code> and displayed active engine version badges in the admin sidebar and blog footer.</li>
<li><strong>Full Multilingual Route Metadata</strong>: Completed <code>hreflang</code> alternates and canonical URL injection across all frontend routes (Home, Category, Tag, Search, Guestbook).</li>
<li><strong>Footer Mobile Readability Polish</strong>: Optimized version badge contrast and layout padding on mobile viewport widths.</li>
</ul>
<hr>
<h3 id="release-v1006">🚀 Release v1.0.0.6</h3>
<ul>
<li><strong>Mobile Core Web Vitals &amp; Performance</strong>: Improved mobile loading performance through dynamic OG image generation, Google Fonts API caching, and asset preloading.</li>
<li><strong>Sitemap 500 Fix</strong>: Removed non-existent legacy tag table queries from the <code>sitemap.xml</code> generator to guarantee uninterrupted sitemap indexing.</li>
<li><strong>Dynamic HTML <code>lang</code> Attribute</strong>: Ensured the root <code>&lt;html&gt;</code> tag dynamically reflects the correct <code>lang</code> attribute (<code>ko</code>, <code>en</code>, <code>ja</code>) based on route context.</li>
<li><strong>i18n Key Architecture</strong>: Standardized language badge translation keys under the <code>common.lang.short_*</code> namespace.</li>
</ul>
<hr>
<h3 id="release-v1005">🚀 Release v1.0.0.5</h3>
<ul>
<li><strong>Svelte 5 Runes Warning Fix</strong>: Resolved reactive state compiler warnings in admin components.</li>
<li><strong>Default Admin Entry Route Refactor</strong>: Streamlined the admin dashboard entry point and feedback flow.</li>
<li><strong>Canonical URL Normalization</strong>: Fixed trailing slash discrepancies and multilingual canonical URL mismatches.</li>
</ul>
<hr>
<h3 id="release-v1004">🚀 Release v1.0.0.4</h3>
<ul>
<li><strong>Naver Webmaster Advisor H1 Fix</strong>: Resolved duplicate <code>&lt;h1&gt;</code> tag detection by search engine bots by implementing Mobile-First SSR layout switching in <code>LayoutRenderer</code>.</li>
<li><strong>Footer Layout Alignment</strong>: Aligned blog footer width and border styles with the header for consistent shadow rendering.</li>
<li><strong>Repository Cleanup</strong>: Removed tracked temporary <code>.bak</code> files from git tracking.</li>
</ul>
]]></content:encoded>
            <category>DevLog</category>
        </item>
        <item>
            <title><![CDATA[Two Methods to Deploy Websites on Cloudflare Pages and Core Concepts]]></title>
            <link>https://testblog-6br.pages.dev/en/general-guide/cloudflare-pages-deployment-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/general-guide/cloudflare-pages-deployment-guide</guid>
            <pubDate>Fri, 21 Aug 2026 23:51:07 GMT</pubDate>
            <description><![CDATA[Learn two authentication methods for deploying websites to Cloudflare Pages (interactive browser login vs. API token) and explore how D1 database, Workers KV storage, and SvelteKit Blog Engine work together seamlessly.]]></description>
            <content:encoded><![CDATA[<h1 id="two-methods-to-deploy-websites-on-cloudflare-pages-and-core-concepts">🚀 Two Methods to Deploy Websites on Cloudflare Pages and Core Concepts</h1>
<p>After building a website or blog, the next big question is: <em>&quot;How do I deploy this to the internet so people can visit?&quot;</em></p>
<p>In the past, you typically had to pay a monthly fee for a Virtual Private Server (VPS), configure an Nginx web server, and manually manage SSL certificates. Today, <strong>Cloudflare Pages (CFP)</strong> allows you to deploy web applications effortlessly with zero server management, blazing-fast global speeds, and a generous free tier.</p>
<p>This guide explains what Cloudflare Pages is, compares <strong>two deployment authentication methods (Interactive Browser Login vs. API Token)</strong>, and introduces the core concepts of <strong>D1 Database</strong> and <strong>Workers KV Storage</strong> from an architectural perspective.</p>
<hr>
<h2 id="1-what-is-cloudflare-pages-cfp">📂 1. What is Cloudflare Pages (CFP)?</h2>
<p>In simple terms, Cloudflare Pages is a platform that <strong>hosts your website across hundreds of Cloudflare data centers worldwide, serving pages to visitors from the closest server at lightning speed.</strong></p>
<p>It supports not only static assets (HTML/CSS/Images) but also full-stack frameworks like SvelteKit and Next.js that perform real-time Server-Side Rendering (SSR) and API routing.<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-en-001.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-en-001"></p>
<h3 id="why-choose-cloudflare-pages-over-traditional-vps">Why Choose Cloudflare Pages Over Traditional VPS?</h3>
<ul>
<li><strong>Zero Server Maintenance</strong>: No need to worry about OS security patches, Nginx config tuning, or monitoring server health. Cloudflare manages the infrastructure 24/7.</li>
<li><strong>Global Edge Performance</strong>: User requests are served from the geographically closest data center, dramatically reducing latency.</li>
<li><strong>Cost-Effective</strong>: Standard personal blogs and portfolios run comfortably within the generous Free Tier.</li>
</ul>
<hr>
<h2 id="2-two-methods-to-deploy-to-cloudflare-from-your-machine">🔑 2. Two Methods to Deploy to Cloudflare from Your Machine</h2>
<p>When running a SvelteKit build (<code>npm run build</code>), <code>@sveltejs/adapter-cloudflare</code> outputs static assets and a server execution worker (<code>_worker.js</code>) into the <strong><code>.svelte-kit/cloudflare</code></strong> directory.</p>
<p>The standard command to deploy this build output is <strong><code>npx wrangler pages deploy .svelte-kit/cloudflare</code></strong>, and there are two primary authentication workflows:</p>
<h4 id="method-1-interactive-browser-login-workflow-wrangler-default">[Method 1] Interactive Browser Login Workflow (Wrangler Default)</h4>
<p>The most intuitive method for beginners with a single account. Running the command opens a web browser to grant permissions.<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-en-002.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-en-002"></p>
<pre><code class="language-bash"># Build SvelteKit and deploy to Pages (interactive browser login)
npm run build
npx wrangler pages deploy .svelte-kit/cloudflare --project-name=my-blog-web --branch=production
</code></pre>
<ul>
<li><strong>How it works</strong>: When you run the deploy command, Wrangler automatically opens your default browser and prompts you to log in to Cloudflare. Once you click [Allow], the authentication session is saved to your local machine (<code>~/.config/.wrangler</code>), and deployment proceeds.</li>
<li><strong>Advantages</strong>: No need to generate API tokens manually; it works seamlessly in a few clicks for single-account personal projects.</li>
<li><strong>Limitations</strong>:<ul>
<li>Switching between multiple Cloudflare accounts requires logging out and in via the browser each time.</li>
<li>Cannot be used in headless environments or CI/CD pipelines (e.g., GitHub Actions).</li>
</ul>
</li>
</ul>
<hr>
<h4 id="method-2-non-interactive-api-token-deployment-standard-for-cicd-amp-multi-account">[Method 2] Non-Interactive API Token Deployment (Standard for CI/CD &amp; Multi-Account)</h4>
<p>The industry standard for multi-account management and automated pipelines. You supply pre-generated tokens via environment variables without browser popups.<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-en-003.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-en-003"></p>
<pre><code class="language-bash"># Deploy with environment variables (PowerShell example)
$env:CLOUDFLARE_API_TOKEN=&quot;YOUR_CLOUDFLARE_API_TOKEN&quot;
$env:CLOUDFLARE_ACCOUNT_ID=&quot;YOUR_ACCOUNT_ID_32_CHARS&quot;
npx wrangler pages deploy .svelte-kit/cloudflare --project-name=my-blog-web --branch=production
</code></pre>
<ul>
<li><strong>How it works</strong>: You generate an API token with required permissions (Pages, D1, KV) in the Cloudflare dashboard. Supplying this token as an environment variable allows Wrangler to authenticate in the background without opening a browser.</li>
<li><strong>Advantages</strong>:<ul>
<li>Runs quietly in the background without popups.</li>
<li>Allows sandboxed, multi-account deployments without session conflicts.</li>
</ul>
</li>
<li><strong>Prerequisite</strong>: Requires creating an API token in the Cloudflare dashboard once beforehand.</li>
</ul>
<hr>
<h2 id="3-where-are-posts-and-images-stored-understanding-d1-and-kv">💾 3. Where Are Posts and Images Stored? (Understanding D1 and KV)</h2>
<p>A dynamic blog requires more than just frontend UI; it needs reliable storage for articles, metadata, and media. Cloudflare provides two dedicated serverless storage solutions:<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-en-004.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-en-004"></p>
<ol>
<li><strong>Cloudflare D1 (Relational SQLite Database)</strong>:<ul>
<li>A distributed SQL database structured into clean rows and columns.</li>
<li>Stores structured data such as post titles, markdown/HTML content, published dates, categories, tags, and site settings with full SQL query capabilities.</li>
</ul>
</li>
<li><strong>Workers KV (Ultra-Fast Key-Value Storage)</strong>:<ul>
<li>A globally distributed key-value store optimized for high-read workloads.</li>
<li>Serves uploaded thumbnails and images across global edge locations with sub-millisecond response times.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="4-architecture-showcase-sveltekit-blog-engine">🧩 4. Architecture Showcase: SvelteKit Blog Engine</h2>
<p>SvelteKit Blog Engine leverages Cloudflare&#39;s serverless infrastructure with an intelligent two-tier architecture:<br><img src="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-en-005.webp" alt="img-general-guide-cloudflare-pages-deployment-guide-en-005"></p>
<hr>
<h3 id="why-separate-blog-and-admin-into-two-independent-pages">Why Separate Blog and Admin into Two Independent Pages?</h3>
<p>Instead of bundling the entire application into a single monolithic app, SvelteKit Blog Engine decouples it into <strong>two standalone Cloudflare Pages projects</strong>:</p>
<ol>
<li><strong>Minimized Attack Surface</strong>: Admin login logic, markdown/rich-text editors, and management API endpoints are completely absent from the public blog bundle. Running on a separate subdomain ensures strong isolation.</li>
<li><strong>Maximum Frontend Performance</strong>: The public blog bundle is extremely lightweight, containing zero admin or editor scripts. This results in blazing-fast First Contentful Paint (FCP) on mobile and desktop devices alike.</li>
</ol>
<hr>
<h3 id="data-flow">Data Flow</h3>
<ul>
<li>When you write an article and upload images in the <strong>Admin Dashboard</strong>, the data is committed to the shared <strong>D1 Database</strong> and <strong>Workers KV</strong>.</li>
<li>When a reader accesses the <strong>Public Blog</strong>, it retrieves the latest post data and images from the same D1 and KV instances to render the page instantly.</li>
</ul>
<hr>
<h2 id="5-conclusion">🚀 5. Conclusion</h2>
<p>Cloudflare Pages empowers developers to run high-performance, full-stack web applications without the burden of server maintenance or costly hosting plans.</p>
<p>For single-site projects, starting with <strong>Interactive Browser Login (<code>wrangler login</code>)</strong> is fast and straightforward. As your infrastructure grows or requires multi-account automation, transitioning to <strong>API Token Deployment</strong> provides a scalable, conflict-free workflow.</p>
]]></content:encoded>
            <category>General Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/cloudflare-pages-deployment-guide/desktop/img-general-guide-cloudflare-pages-deployment-guide-en-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[[Dev Log] 2026-07-10]]></title>
            <link>https://testblog-6br.pages.dev/en/devlog/devlog-2026-07-10</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/devlog/devlog-2026-07-10</guid>
            <pubDate>Sun, 26 Jul 2026 04:50:21 GMT</pubDate>
            <description><![CDATA[v1.0.0.2 & v1.0.0.3 Release Notes & New Theme Adjustments]]></description>
            <content:encoded><![CDATA[<h2 id="v1002-update-details">v1.0.0.2 Update Details</h2>
<p>This release includes several enhancements to improve the usability and functionality of the admin post editor.</p>
<h3 id="key-changes">🚀 Key Changes</h3>
<ol>
<li><p><strong>Added Image Link Insertion and Editing in Post Body</strong></p>
<ul>
<li>Expanded the modal UI to allow linking custom URLs and configuring &quot;Open in new tab&quot; settings when inserting or editing images in the editor.</li>
<li>Integrated HTML pre/post-processing to prevent Tiptap editor from forcibly removing image link attributes upon loading.</li>
</ul>
</li>
<li><p><strong>Visualized Semantic Image Captions in Preview Mode</strong></p>
<ul>
<li>Synchronized body text processing utilities so that semantic image captions (<code>&lt;figcaption&gt;</code>) are rendered correctly in the post preview tab.</li>
</ul>
</li>
<li><p><strong>Integrated Real-time Character Counter with Multilingual Support</strong></p>
<ul>
<li>Added a real-time character counter UI at the bottom of the post editor/edit screen.</li>
<li>Automatically strips HTML tags and Markdown syntax to count raw text characters accurately with full multilingual support.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="v1003-update-details">v1.0.0.3 Update Details</h2>
<p>This release completely resolves structural flaws in the widget system and unifies the UI list styles for widgets.</p>
<h3 id="bug-fixes">🐛 Bug Fixes</h3>
<ol>
<li><p><strong>Fixed Rendering Crash When Placing Newly Created Widgets</strong></p>
<ul>
<li>Fixed a critical crash where placing a newly created widget directly onto a layout caused rendering failure due to a missing <code>master-</code> prefix upon saving.</li>
</ul>
</li>
<li><p><strong>Fixed Data Loss of Widget <code>config</code> Across Desktop/Mobile Layouts</strong></p>
<ul>
<li>Fixed a bug where widget configuration settings (<code>config</code>) were completely omitted during layout placement, causing placed widgets to save as empty states.</li>
</ul>
</li>
<li><p><strong>Fixed <code>config</code> Reset Bug Upon Opening Widget Edit Modal</strong></p>
<ul>
<li>Fixed a bug where opening the edit modal for a placed widget triggered a <code>SyntaxError</code> by executing duplicate <code>JSON.parse</code> calls on an already parsed <code>config</code> object, resetting all modal settings (limit, shadow, etc.) to defaults.</li>
</ul>
</li>
<li><p><strong>Strengthened Input Validation for Invalid <code>limit</code> Values</strong></p>
<ul>
<li>Blocked vulnerability where invalid <code>limit</code> values (<code>NaN</code>, less than 1) could be saved during widget creation/editing, normalizing invalid inputs to the default value of <code>5</code>.</li>
</ul>
</li>
<li><p><strong>Fixed Data Query Scope Error with Multiple Widgets of the Same Type</strong></p>
<ul>
<li>Fixed a structural bug where multiple widgets of the same type (e.g., Popular Posts) queried the database using only the first widget&#39;s <code>limit</code>, truncating data for the remaining widgets. Database queries now fetch up to the maximum <code>limit</code> among identical widgets, allowing each widget to render its data independently according to its own settings.</li>
</ul>
</li>
</ol>
<h3 id="styling">🎨 Styling</h3>
<ol>
<li><strong>Unified List Divider Styles for Widgets</strong><ul>
<li>Resolved visual inconsistencies where <code>RecentPostsWidget</code> and <code>PopularPostsWidget</code> used different item divider styles. Both widgets are now unified with a <code>border-bottom</code> divider and <code>0.5rem</code> padding.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="minor-layout-amp-design-adjustments">🎨 Minor Layout &amp; Design Adjustments</h2>
<p>Refined overall style elements including blog layout and colors so that screen components blend naturally without breaking.</p>
<h3 id="key-style-adjustments">🛠️ Key Style Adjustments</h3>
<ol>
<li><p><strong>Adjusted Theme Colors and Tones</strong></p>
<ul>
<li>Set an orange secondary color as an accent point while refining background colors, card borders, and shadow values to reduce visual fatigue.</li>
</ul>
</li>
<li><p><strong>Optimized Desktop 2-Column Layout Alignment</strong></p>
<ul>
<li>Re-adjusted the width ratio between the main content area and sidebar (categories, recent/popular posts, etc.) to ensure left/right margins and content alignment match smoothly.</li>
</ul>
</li>
<li><p><strong>Aligned Header/Footer &amp; Mobile Menu Elements</strong></p>
<ul>
<li>Cleaned up top header transparency settings alongside mobile menu icon alignment and footer layout elements for a crisp presentation.</li>
</ul>
</li>
</ol>
]]></content:encoded>
            <category>DevLog</category>
        </item>
        <item>
            <title><![CDATA[[Devlog] How I Ended Up Building an Outdated Blog Engine]]></title>
            <link>https://testblog-6br.pages.dev/en/devlog/devlog-why-i-built-a-blog-engine-in-2026</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/devlog/devlog-why-i-built-a-blog-engine-in-2026</guid>
            <pubDate>Sun, 26 Jul 2026 04:50:02 GMT</pubDate>
            <description><![CDATA[Just Wanted to Earn Coffee Money, Ended Up Building a Blog Engine]]></description>
            <content:encoded><![CDATA[<p><em>Please note: This post is written in a casual, monologue style to organize my personal thoughts.</em></p><p><strong>1.</strong><br>To be honest, YouTube is largely to blame for how things turned out(?).<br>When I decided to try my hand as a solo developer later in life, I was flooded with an overwhelming amount of information.<br>The problem is that while the quantity of information is overflowing, the quality is often terrible.</p><p><strong>2.</strong><br>If it's just basic introductory stuff, a high volume of info isn't really a problem. But the moment things get slightly specialized or require a specific niche, I encounter completely useless information that leaves me wondering why it even popped up in my search results. Or worse, the title perfectly matches what I want, but the actual content is just a meaningless sequence of text generated to form valid sentences.<br>When this happens on a YouTube video, it genuinely makes me furious.</p><p><strong>3.</strong><br>Then, like finding an oasis in the desert, the rare pieces of truly valuable information almost always came from developer blog posts.<br>I felt so relieved, grateful, and inspired. And then, my focus—which makes me suspect I might have adult ADHD—led me to a sudden decision:&nbsp;<em>"Hey, maybe I should build a dev blog too, help someone out, and maybe earn enough for a cup of coffee(?)."</em></p><p><strong>4.</strong><br>So I started evaluating existing blogging platforms, but every single one of them had a dealbreaker for me.<br>First, I ruled out anything that required hosting or maintenance costs.<br>Sure, earning a little coffee money would be nice, but I know that to actually pull that off, you have to work ridiculously hard. And I know myself—I'm just not that hard-working.<br>So right from the start, my absolute baseline rule was:&nbsp;<strong>Zero setup or recurring maintenance costs.</strong><br>My initial plan was to host WordPress on a local Proxmox server.<br>To run that safely, I figured minimum security measures like an L2 switch and a firewall like pfSense were essential.<br>After setting all that up and looking at WordPress again, I realized customization was way too hard. When I realized I'd have to properly study PHP just to customize it, I abandoned that approach.</p><p><strong>5.</strong><br>I quickly looked for alternative solutions, but every option had its own catch.<br>No matter what solution I picked, having to study something completely new just to set it up felt mentally exhausting.<br>That mental fatigue eventually led me to a reckless thought:&nbsp;<em>"Screw it, I'll just build a simple one myself."</em></p><p><strong>6.</strong><br>At the time, I was already messing around quite a bit with Svelte and SvelteKit.<br>Building a basic blog was easy enough. The real issue was that I couldn't trust my own design skills.<br>So I decided to architect it in a way that made swapping designs effortless—and that rabbit hole kept rolling until it became what it is today.</p><p><strong>7.</strong><br>In reality, I spent the vast majority of my time polishing the design editor and debugging.<br>Initially, I set out to build a "simple blogging tool" and finished about 90% of the code in just one day.<br>Then I actually built a blog with it and started running it.<br>That’s when the feature creep hit:&nbsp;<em>"Oh, I need this too," "I should add this feature," "It'd be nice to have this," "This is a must-have."</em>&nbsp;It felt like an endless flood of ideas.<br>What started as a mini-project in late November last year finally wrapped up feature implementation in March this year, and only in July did I finally open-source it on GitHub.</p><p><strong>8.</strong><br>Before I knew it, the codebase had grown way too large and complex, forcing me to modularize and refactor along the way.<br>I even overhauled the system architecture multiple times.<br>Initially, the admin panel was hosted on a Proxmox server accessible only locally, while the DB and auth relied on Supabase.<br>However, after experiencing terrible cold starts and seeing the project name exposed during auth on Supabase's free plan, I pivoted to going all-in on the Cloudflare ecosystem and integrated Better-auth, which I had used before.<br>After that, I continued making major changes to the internal logic and architecture.<br>On top of that, working in a Windows environment meant that every single code tweak required a full deployment to verify, leading to an astronomical amount of trial and error.</p><p><strong>9.</strong><br>Near the finish line, the codebase became so massive that I couldn't handle modifications alone anymore, so while I wrote a lot of it, I relied heavily on Gemini for the rest.<br>Since I hadn't documented anything, I had to compare the actual running features one by one to write the docs—and whenever I spotted a bug, I went right back into fixing code.<br>From March to July, it was an endless loop of:&nbsp;<em>Documentation ➔ Bug Discovery ➔ Bug Fixing.</em><br>Meanwhile, thoughts like&nbsp;<em>"Wait, I need this feature too!"</em>&nbsp;and&nbsp;<em>"Hold on, where did that feature I built go?"</em>&nbsp;kept popping up, driving me absolutely insane.</p><p><strong>10.</strong><br>To make matters worse, Gemini frequently lost its mind.<br>Instead of fixing a single line of code, it would tear down completely unrelated modules and rewrite them. I'm convinced that if Gemini hadn't thrown so many tantrums, I would have finished at least two months earlier.<br>Anyway, right up to the GitHub release, I was under extreme stress until I finally decided to cut out all the&nbsp;<em>"I should add this"</em>&nbsp;features and just ship whatever was already working and cleaned up.<br>That became v1.0.0.0.</p><p><strong>11.</strong><br>And right after that, v1.0.0.1 was released.<br>I thought I only shipped the "cleaned up working parts," but I immediately noticed things that hadn't been cleaned up properly.<br>I'm fairly certain there are more hidden issues like this, which makes me a bit anxious. I guess I had a hunch even before uploading, which is why I prepared the&nbsp;<code>v1.0.0.x</code>&nbsp;versioning ahead of time.</p><p><strong>12.</strong><br>And that is how the SvelteKit Blog Engine came to be!<br>Ending a post is always awkward, so I'll wrap up by sharing my PageSpeed Insights score.<br>AdSense is currently pending approval, but the code script is already embedded. Also, CDN caching is set to a 2-minute TTL, so if a test runs while the cache is purged and regenerating, it might impact performance—though I'm not 100% sure of the exact cause, the scores fluctuate quite a bit.<br>Please treat these scores as a rough reference from an average run.<br>Once AdSense gets approved, I'll post an updated PageSpeed Insights report.<br>Have a great day! :)</p><figure data-align="center"><img src="https://sveltekitblog.com/images/posts/devlog-why-i-built-a-blog-engine-in-2026/desktop/img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-001.webp" alt="img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-001" data-align="center" data-caption="Mobile score. (Integrated with AdSense [pending approval], GA4, Google Search Console, and Naver Search Advisor)"><figcaption>Mobile score. (Integrated with AdSense [pending approval], GA4, Google Search Console, and Naver Search Advisor)</figcaption></figure><figure data-align="center"><img src="https://sveltekitblog.com/images/posts/devlog-why-i-built-a-blog-engine-in-2026/desktop/img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-002.webp" alt="img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-002" data-align="center" data-caption="Desktop score. (Integrated with AdSense [pending approval], GA4, Google Search Console, and Naver Search Advisor)"><figcaption>Desktop score. (Integrated with AdSense [pending approval], GA4, Google Search Console, and Naver Search Advisor)</figcaption></figure><p></p>]]></content:encoded>
            <category>DevLog</category>
            <enclosure url="https://sveltekitblog.com/images/posts/devlog-why-i-built-a-blog-engine-in-2026/desktop/img-devlog-devlog-why-i-built-a-blog-engine-in-2026-ko-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[CMD One-Click Installation & Cloudflare Deployment Guide]]></title>
            <link>https://testblog-6br.pages.dev/en/admin-guide/admin-install-and-deploy</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/admin-guide/admin-install-and-deploy</guid>
            <pubDate>Wed, 15 Jul 2026 12:19:35 GMT</pubDate>
            <description><![CDATA[Learn how to build the Cloudflare edge infrastructure and deploy/sync both admin and blog apps simultaneously using a single interactive setup script in a terminal (CMD/PowerShell) environment.]]></description>
            <content:encoded><![CDATA[<h1 id="cmd-one-click-installation-amp-cloudflare-deployment-guide">🚀 CMD One-Click Installation &amp; Cloudflare Deployment Guide</h1>
<p>This guide explains the easiest and safest way to build the Cloudflare edge infrastructure and deploy/sync both the admin and blog apps simultaneously by running a single automation command (<code>npm run setup</code>) in a terminal (CMD or PowerShell) environment.</p>
<hr>
<h2 id="1-introduction-to-one-click-deployment-automation">🛠️ 1. Introduction to One-Click Deployment Automation</h2>
<p>The SvelteKit blog engine has fully automated the complex process of server resource creation and configuration file editing through an interactive setup tool in the terminal. </p>
<p>After unpacking the blog source code or downloading it to your computer via <code>git clone</code>, open the terminal, navigate to the project root folder, and run the following commands in sequence:</p>
<pre><code class="language-bash"># Install required packages
npm install
</code></pre>
<ul>
<li><strong>Dependency Library Installation</strong>: Enter <code>npm install</code> in the terminal to install all required packages. If a security warning (vulnerabilities) message appears in the terminal after installation is complete, it is recommended to enter the <code>npm audit fix</code> command to safely apply the latest security patches.</li>
</ul>
<p>Once the package installation is complete and you run <code>npm run setup</code>, the following processes will be handled automatically according to the terminal prompts:</p>
<ul>
<li><strong>Easy Cloudflare Account Authentication</strong>: Link your deployment account with a single click.</li>
<li><strong>Create DB and Storage Resources</strong>: Automatically create 2 D1 databases dedicated to the blog and a KV store for storing images.</li>
<li><strong>Real-time Configuration File Update</strong>: Detect the unique IDs of the created databases and automatically inject them into the monorepo configuration files (<code>wrangler.json</code>).</li>
<li><strong>Database Seed Data Injection</strong>: Create tables and automatically register sample posts and basic configuration information according to the selected default language.</li>
<li><strong>Upload Secrets and Allowed IP</strong>: Automatically transmit the deployer&#39;s current public IP address (<code>ALLOWED_IP</code>) and password environment variables to the cloud for admin access.</li>
<li><strong>Integrated Build &amp; Web Service Deployment</strong>: Bundle both the blog and admin SvelteKit projects and immediately deploy them to Cloudflare Pages.</li>
</ul>
<hr>
<h2 id="2-pre-deployment-checklist">📋 2. Pre-deployment Checklist</h2>
<p>Please make sure all of the following are prepared before starting the safe installation:</p>
<ol>
<li><strong>Install Node.js</strong>: Node.js (version 22 or higher) must be working on your computer.</li>
<li><strong>Cloudflare Account</strong>: A Cloudflare account (the free tier is sufficient) is required to host the blog site and databases.</li>
<li><strong>Create Secret Environment Variable Files (.dev.vars) (Required)</strong>:<ul>
<li>Before starting deployment, you must <strong>manually create</strong> a <code>.dev.vars</code> file in each of the <code>apps/admin/</code> and <code>apps/blog/</code> folders.</li>
<li>In the <code>apps/admin/.dev.vars</code> file, write <code>ADMIN_PASSWORD=YourAdminLoginPassword</code>.</li>
<li>In the <code>apps/blog/.dev.vars</code> file, write <code>BETTER_AUTH_SECRET=ArbitraryStringOfYourChoice</code>.</li>
<li><blockquote>
<p>[!WARNING]<br>If these environment variables are not set, the <strong>installation tool will stop</strong> immediately for correct operation upon running <code>npm run setup</code>.</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<hr>
<h2 id="3-step-by-step-guide-for-npm-run-setup">⚙️ 3. Step-by-Step Guide for npm run setup</h2>
<p>Open the terminal, type <code>npm run setup</code> in the blog project root directory, and press Enter.</p>
<h3 id="step-0-secrets-pre-validation"><strong>Step 0. Secrets Pre-Validation</strong></h3>
<ul>
<li>Immediately upon execution, it checks if the <code>.dev.vars</code> files and the required secret variables (<code>ADMIN_PASSWORD</code>, <code>BETTER_AUTH_SECRET</code>) are entered in the admin and blog folders.</li>
<li>If any value is missing, the guide log is displayed and the script exits, so please make sure to fill them in advance.</li>
</ul>
<h3 id="step-1-configure-deployment-project-name-domain-url"><strong>Step 1. Configure Deployment Project Name (Domain URL)</strong></h3>
<ul>
<li>This step defines the URL that readers will use to access your blog (e.g., <code>[entered-project-name].pages.dev</code>).</li>
<li>It takes two names: one for the blog and one for the admin app. If running in restore mode (<code>--restore</code>), it automatically reads the existing names from the backup configuration file (<code>wrangler.backup.json</code>).</li>
</ul>
<h3 id="step-2-cloudflare-account-authentication"><strong>Step 2. Cloudflare Account Authentication</strong></h3>
<ul>
<li>Verifies Cloudflare authentication. If you have logged in previously, it automatically detects the session and skips this step.</li>
<li>If you are not logged in, a browser window will open showing the authentication request screen. Click <strong>[Allow]</strong> to approve the account integration.</li>
<li><em>Security Information: This automation tool only requests the minimum account API permissions required for infrastructure creation and deployment, so you can proceed with confidence.</em></li>
</ul>
<h3 id="step-3-resource-creation-amp-configuration-binding"><strong>Step 3. Resource Creation &amp; Configuration Binding</strong></h3>
<ul>
<li>This step creates the database and media storage in the cloud for the actual service to run. Look at the prompts in the terminal and enter the appropriate number.<ol>
<li><strong><code>1</code> (Fresh Install)</strong>: <strong>Completely deletes</strong> previously created databases and storage and recreates them. Please note that all existing data will be lost.</li>
<li><strong><code>2</code> (Keep Existing Data) [Recommended]</strong>: If resources already exist, it safely preserves the data and only links the connection information.</li>
</ol>
</li>
<li>Upon completion, the newly issued unique DB and KV IDs are automatically updated in each configuration file (<code>wrangler.json</code>) of the monorepo.</li>
</ul>
<h3 id="step-4-pre-create-web-projects"><strong>Step 4. Pre-create Web Projects</strong></h3>
<ul>
<li>To improve deployment reliability, it pre-registers and reserves empty web projects on Cloudflare Edge.</li>
<li>If a domain with the same name is already occupied, it <strong>reuses the existing domain</strong> and automatically transitions to the next step safely.</li>
</ul>
<h3 id="step-5-remote-sync-of-secrets"><strong>Step 5. Remote Sync of Secrets</strong></h3>
<ul>
<li>Safely uploads and synchronizes the secret values, such as the master password written locally in <code>.dev.vars</code>, to Cloudflare.</li>
<li><strong>Automatic Access IP Registration</strong>: Especially in this step, it detects the public IP address of the administrator&#39;s computer running the deployment in real-time and automatically injects it as the <code>ALLOWED_IP</code> variable. This prevents the administrator from being blocked upon accessing the login screen immediately after deployment.</li>
</ul>
<h3 id="step-6-database-table-configuration-amp-default-theme-setup"><strong>Step 6. Database Table Configuration &amp; Default Theme Setup</strong></h3>
<ul>
<li>Configure the database tables needed for the blog to function and inject default settings.</li>
<li>Following the on-screen prompts, choose your primary language (Korean, English, Japanese). This sets up the translation system, sample categories, header menu structure, and a default welcome post automatically.</li>
</ul>
<h3 id="step-7-build-web-services-amp-final-deployment"><strong>Step 7. Build Web Services &amp; Final Deployment</strong></h3>
<ul>
<li>Performs the integrated build of all monorepo apps and automatically runs the deployment command to Cloudflare servers to complete the installation process. Once deployment is complete, the Pages URLs for both the blog and admin will be displayed.</li>
</ul>
<hr>
<blockquote>
<p>[!TIP]</p>
<h3 id="automatic-secret-settings-and-immediate-connection-support">💡 Automatic Secret Settings and Immediate Connection Support</h3>
<p>The installation process of this project seamlessly handles resource creation, web project provisioning, and secret environment variable transmission.<br>Upon completion, the login password and IP permission settings are perfectly configured on the remote server, so it works immediately without any additional manual configuration.</p>
</blockquote>
<hr>
<h2 id="4-blog-update-amp-data-restore-deployment-npm-run-restore">🔄 4. Blog Update &amp; Data Restore Deployment (npm run restore)</h2>
<p>When a <strong>new patch version or a bug-fixed release code is deployed</strong> while running your blog, this process allows you to safely transfer your existing post data and URLs without data loss while replacing the screen with the new code.</p>
<h3 id="4-step-migration-procedure-for-new-versions">💡 4-Step Migration Procedure for New Versions</h3>
<p>You can safely complete the upgrade without data loss by using the admin data backup and the <strong><code>npm run restore</code></strong> command.</p>
<ol>
<li><strong>[Step 1] Backup Existing Data &amp; Deployment Settings</strong>:<ul>
<li>Access the <strong><code>Backup</code></strong> menu and the backup section at the bottom of the <strong><code>Theme Editor</code></strong> menu of your active admin app, and download the post content data and theme design settings to your computer respectively.</li>
<li>Also, click the &#39;Download Settings Backup&#39; button in the <strong><code>Backup</code></strong> menu to save the [wrangler.backup.json] file to your PC. (This is the most critical file for maintaining the link to your existing production database&#39;s unique ID.)</li>
</ul>
</li>
<li><strong>[Step 2] Isolated Testing of New Code</strong>:<ul>
<li>In the newly downloaded version&#39;s code folder, run <code>npm run setup</code> and enter a <strong>temporary DB for testing</strong> and a <strong>temporary Pages deployment name for testing</strong> to deploy it separately.</li>
<li>Log in to the newly deployed test admin, and import the backup data files saved in Step 1 to verify that the data loads successfully in advance.</li>
</ul>
</li>
<li><strong>[Step 3] Restore Bindings to Existing Production Server (<code>npm run restore</code>)</strong>:<ul>
<li>Once verification is complete, copy and paste the <code>wrangler.backup.json</code> file saved in Step 1 into the root directory of the new version&#39;s project folder.</li>
<li>Run the <strong><code>npm run restore</code></strong> command in the terminal. The tool script will read the <strong>existing production DB and KV unique ID information</strong> written in the backup file and automatically connect them to the new code&#39;s configuration files via override.</li>
</ul>
</li>
<li><strong>[Step 4] Schema Synchronization &amp; Override Deployment</strong>:<ul>
<li>The restore script safely updates only the table structure schema of the existing production DB to the latest specifications, so <strong>actual posts or data already stored will not be corrupted or deleted</strong> and will be perfectly preserved.</li>
<li>Next, run <code>npm run deploy:blog</code> and <code>npm run deploy:admin</code> respectively. The latest upgraded blog system will be safely deployed, overwriting the system while keeping the same URLs you were using.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="5-backup-file-types-and-functions">📦 5. Backup File Types and Functions</h2>
<p>Refer to the table below for the roles of individual files that can be safely imported and exported from the admin backup menu.</p>
<table>
<thead>
<tr>
<th align="left">Backup Type</th>
<th align="left">Extraction Path</th>
<th align="left">Recommended Filename Example</th>
<th align="left">Detailed Feature &amp; Preserved Content</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>1. Content DB Data</strong></td>
<td align="left"><strong><code>Backup</code></strong> Menu</td>
<td align="left"><code>blog-content-backup-[date].json</code></td>
<td align="left">Preserves all written blog post content, created category information, tag lists, and basic system settings.</td>
</tr>
<tr>
<td align="left"><strong>2. Deployment Config Backup</strong></td>
<td align="left"><strong><code>Backup</code></strong> Menu</td>
<td align="left"><code>wrangler.backup.json</code></td>
<td align="left">Safely preserves the deployment name and unique UUID information required for Cloudflare D1 DB and KV storage binding (<strong>Essential for server migration</strong>).</td>
</tr>
<tr>
<td align="left"><strong>3. Media File Backup</strong></td>
<td align="left"><code>Media Backup &amp; Restore</code> section in the <strong><code>Backup</code></strong> menu<br>or <code>[Backup / Restore]</code> button in the top right of the <strong><code>Media Library</code></strong> menu</td>
<td align="left"><code>[storage-name]-images-backup-[date].zip</code> <br>(e.g., <code>r2-images-backup-[date].zip</code>)</td>
<td align="left">Downloads and preserves all image media files uploaded to the active image storage (R2, Supabase, etc.) as a ZIP file.</td>
</tr>
<tr>
<td align="left"><strong>4. Design Settings Backup</strong></td>
<td align="left">Bottom of <strong><code>Theme Editor</code></strong> menu</td>
<td align="left"><code>blog-design-backup-[date].json</code></td>
<td align="left">Preserves blog theme color information, background types, and device-specific (desktop/mobile) widget layout structures configured in the Theme Editor.</td>
</tr>
<tr>
<td align="left"><strong>5. Full System Backup</strong></td>
<td align="left">Bottom of <strong><code>Site Settings</code></strong> menu</td>
<td align="left"><code>full-system-backup-[date].json</code></td>
<td align="left">Gathers raw data of all tables from both databases (BLOG_DB, USER_DB) into a single JSON file for a complete backup and restore.</td>
</tr>
</tbody></table>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-en-001.webp" alt="img-admin-guide-admin-install-and-deploy-en-001" data-align="left" data-caption="Content DB Data" /><figcaption>Content DB Data</figcaption></figure>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-en-002.webp" alt="img-admin-guide-admin-install-and-deploy-en-002" data-align="left" data-caption="Deployment Config Backup" /><figcaption>Deployment Config Backup</figcaption></figure>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-en-003.webp" alt="img-admin-guide-admin-install-and-deploy-en-003" data-align="left" data-caption="Media File Backup" /><figcaption>Media File Backup</figcaption></figure>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-en-004.webp" alt="img-admin-guide-admin-install-and-deploy-en-004" data-align="left" data-caption="Design Settings Backup" /><figcaption>Design Settings Backup</figcaption></figure>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-en-005.webp" alt="img-admin-guide-admin-install-and-deploy-en-005" data-align="left" data-caption="Full System Backup" /><figcaption>Full System Backup</figcaption></figure>]]></content:encoded>
            <category>Admin Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-install-and-deploy/desktop/img-admin-guide-admin-install-and-deploy-en-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[First Access to Admin & Getting Started with Configurations]]></title>
            <link>https://testblog-6br.pages.dev/en/admin-guide/admin-getting-started</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/admin-guide/admin-getting-started</guid>
            <pubDate>Wed, 15 Jul 2026 12:19:26 GMT</pubDate>
            <description><![CDATA[Learn how to safely access the admin app, maintain login sessions, and utilize the multilingual UI dictionary to manage the blog system.]]></description>
            <content:encoded><![CDATA[<h1 id="first-access-to-admin-amp-getting-started-with-configurations">🔑 First Access to Admin &amp; Getting Started with Configurations</h1>
<p>This document guides administrators on how to safely access the admin page, maintain login sessions, and easily manage various site-wide text strings (multilingual dictionary) to control the blog system.</p>
<hr>
<h2 id="1-ip-security-control-policy-before-admin-login">🔒 1. IP Security Control Policy before Admin Login</h2>
<p>This blog engine adheres to a strict <strong>Access IP Filtering</strong> security policy to completely prevent unauthorized external access or hijacking of posting permissions. Only the administrator&#39;s public IP address detected at the time of deployment is registered in the server&#39;s allowlist (<code>ALLOWED_IP</code>) to permit access.</p>
<blockquote>
<p>[!IMPORTANT]</p>
<h3 id="safe-zone-centered-operation-principle-no-use-in-public-places">🛡️ Safe-Zone Centered Operation Principle (No Use in Public Places)</h3>
<p>Admin management of this blog must be performed only in <strong>places where physical and network security are secured, such as home or a trusted private office.</strong><br><strong>To prevent security leaks, accessing the admin page is strictly discouraged and should be avoided in public places such as PC cafes (PC bangs), libraries, or under public Wi-Fi networks, as they are exposed to high security risks.</strong></p>
</blockquote>
<p>If you encounter a <code>403 Forbidden</code> block screen because your public IP address changed due to router rebooting or network conditions within your safe zone, you must manually update the allowlist according to the following procedure.</p>
<h3 id="allowed-ip-list-renewal-procedure-redeployment-required">⚙️ Allowed IP List Renewal Procedure (Redeployment Required)</h3>
<ol>
<li>Open the terminal under the changed internet environment, and run the command to <strong>redeploy</strong> the blog service admin app once again.</li>
<li><strong>Workflow Guide</strong>: While this is safer than forcedly editing the database internals (which is a risky operation), it is a somewhat tedious and inconvenient process because it requires the build process and file upload waiting time of several minutes each time.</li>
<li>While the deployment script is running, it detects the new public IP address of your currently connected computer and replaces the remote server&#39;s allowed IP list with the latest one.</li>
<li>Once the deployment process is finalized, reconnecting to the admin URL will re-enable the normal login screen.</li>
<li>You only need to redeploy the admin page.</li>
</ol>
<hr>
<h2 id="2-admin-login-amp-session-management">🔑 2. Admin Login &amp; Session Management</h2>
<p>Once the login block is normal, enter the master password (<code>ADMIN_PASSWORD</code>) configured during setup to log in.</p>
<ul>
<li><strong>Secure Session Maintenance</strong>: By utilizing dedicated secure cookies, the admin login session remains safely maintained for 30 days.</li>
<li><strong>Prohibition of Access from Public Computers and Risky Areas</strong>:<br>The most secure practice is to never attempt logging in to the admin console on unverified public computers or PC cafes. If you must log in under public conditions, make sure to click the <strong>[Logout]</strong> button at the bottom left of the admin page immediately after completing work, and also enter <code>npx wrangler logout</code> in your terminal to completely sign out from the Cloudflare management credentials on that computer to prevent any credential leaks.</li>
</ul>
<blockquote>
<p>[!WARNING]</p>
<h3 id="minimize-exposure-of-admin-access-address-domain">🔒 Minimize Exposure of Admin Access Address (Domain)</h3>
<p>While the admin console is double-protected by the IP allowlist, minimizing the attack surface itself is the most robust security practice.</p>
<p>Therefore, we strongly recommend that you do not bind an obvious custom domain like <code>admin.myblog.com</code>, but instead use the randomized subdomain URL (e.g., <code>[project-name].pages.dev</code>) provided default by Cloudflare Pages to keep the admin entry hidden.</p>
</blockquote>
<hr>
<h2 id="3-multilingual-dictionary-i18n-settings-guide">🌐 3. Multilingual Dictionary (i18n) Settings Guide</h2>
<p>Fixed common UI text strings (menu labels, comment buttons, login prompts, etc.) outside of post contents can be modified instantly in English, Korean, or Japanese using the admin dictionary editor without editing a single line of source code.<br><figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-getting-started/desktop/img-admin-guide-admin-getting-started-en-002.webp" alt="img-admin-guide-admin-getting-started-en-002" data-align="left" /></figure></p>
<p>You can register a new language by using the <strong><code>Add Language</code></strong> feature. However, for the newly added language to display correctly on the site, you must translate all existing dictionary keys into that language.<br><figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-getting-started/desktop/img-admin-guide-admin-getting-started-en-001.webp" alt="img-admin-guide-admin-getting-started-en-001" data-align="left" /></figure></p>
<ol>
<li>Go to the <strong><code>Languages</code></strong> menu on the left sidebar of the admin console.</li>
<li>A list of all dictionary keys and their saved translations used across the site is provided in the <strong><code>UI Dictionary Editor</code></strong> section at the bottom.</li>
<li>Enter your desired text (Korean, English, Japanese) directly into the input field for the key you wish to modify, and click the <strong><code>Save</code></strong> icon on the far right of that row.</li>
<li>Changes apply to the live site immediately upon saving. When visitors switch languages on the blog, the modified texts will be displayed seamlessly in real-time.</li>
</ol>
]]></content:encoded>
            <category>Admin Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-getting-started/desktop/img-admin-guide-admin-getting-started-en-002.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Admin Core Features and Dual Editor Overview]]></title>
            <link>https://testblog-6br.pages.dev/en/admin-guide/admin-core-features</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/admin-guide/admin-core-features</guid>
            <pubDate>Wed, 15 Jul 2026 12:19:17 GMT</pubDate>
            <description><![CDATA[A brief introduction to core admin features including multi-language simultaneous saving, visual and markdown dual editor switching, and device-specific widget placement.]]></description>
            <content:encoded><![CDATA[<h1 id="admin-core-features-and-dual-editor-overview">🎨 Admin Core Features and Dual Editor Overview</h1>
<p>This document provides a brief overview of the core features in the admin console, including multi-language simultaneous publishing, device-specific layout configuration, and real-time design theme settings.</p>
<hr>
<h2 id="1-multi-language-writing-and-dual-editor-support">📝 1. Multi-Language Writing and Dual Editor Support</h2>
<p>When entering the writing menu, a multi-language editor layout is provided, with multiple language tabs arranged side-by-side on a single screen.</p>
<h3 id="batch-multi-language-writing-and-saving">① Batch Multi-Language Writing and Saving</h3>
<ul>
<li><strong>Writing Flow</strong>: Switch between the language tabs (KO, EN, JA, etc.) at the top to write titles, excerpts, slugs (URL paths), and body content for each language.</li>
<li><strong>Batch Saving</strong>: Click the <strong>[Save All Tabs Simultaneously]</strong> button at the bottom to save the content of all languages to the database at once. Languages not explicitly set to <strong>Publish</strong> will be saved as <strong>Drafts</strong>, and any empty language tabs will be skipped.</li>
</ul>
<h3 id="dual-editor-support-visual-vs-markdown">② Dual Editor Support (Visual vs. Markdown)</h3>
<ul>
<li><strong>Visual HTML Editor (Visual)</strong>: A rich-text editor that allows direct formatting and media embedding. Image files can be easily uploaded and inserted via the toolbar button.</li>
</ul>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-en-002.webp" alt="img-admin-guide-admin-core-features-en-002" data-align="left" /></figure><ul>
<li><strong>Markdown Editor (Markdown)</strong>: Provided for users who prefer markdown syntax. Metadata such as titles, excerpts, categories, and tags are automatically managed in the header using YAML Front Matter (<code>---</code>). Both markdown source and parsed HTML are stored in the database upon saving.</li>
</ul>
<figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-en-003.webp" alt="img-admin-guide-admin-core-features-en-003" data-align="left" /></figure><h3 id="automatic-thumbnail-selection">③ Automatic Thumbnail Selection</h3>
<ul>
<li>If no thumbnail image is manually specified, the system automatically analyzes the content to assign a representative image:<ul>
<li><strong>1st Priority</strong>: The first image element found in the body text.</li>
<li><strong>2nd Priority</strong>: If no images are present but a YouTube video link or iframe embed is found, the official high-resolution YouTube thumbnail URL of the video is used.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-device-specific-desktopmobile-independent-widget-placement">🧱 2. Device-Specific (Desktop/Mobile) Independent Widget Placement</h2>
<p>The layout editor supports drag-and-drop widget arrangement for sidebars and content areas, allowing you to configure independent widget exposure based on whether visitors are on desktop or mobile devices.<br><figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-en-001.webp" alt="img-admin-guide-admin-core-features-en-001" data-align="left" data-caption="Desktop/Mobile Mode Selection Icons" /><figcaption>Desktop/Mobile Mode Selection Icons</figcaption></figure></p>
<h3 id="device-exposure-options">⚙️ Device Exposure Options</h3>
<ol>
<li>Choose the column layout and width ratio in the <strong><code>Blog Structure</code></strong> tab under the <strong><code>Design Editor</code></strong> menu.</li>
<li>Assign device exposure conditions (Device) when adding or modifying widgets:<ul>
<li><strong>Desktop</strong>: Displays the widget only on wider PC screens, preventing unnecessary resource loads on mobile devices.</li>
<li><strong>Mobile</strong>: Hides the widget on PC screens and limits visibility to mobile viewport resolutions.</li>
</ul>
</li>
<li><strong>Effect</strong>: Skipping heavy or redundant widget rendering for mobile visitors optimizes page load times and mobile scrolling performance.</li>
</ol>
<hr>
<h2 id="3-real-time-design-editor-and-theme-settings">🎨 3. Real-Time Design Editor and Theme Settings</h2>
<p>Design theme changes and background configurations apply instantly via CSS variables in the visitor&#39;s browser without requiring server rebuilds or redeployments.</p>
<ul>
<li><strong>4 Background Types</strong>: Select from Solid color, Gradient, Background Image, or interactive HTML5 Canvas animations (Canvas).</li>
<li><strong>Glassmorphism Effect</strong>: When using background images, adjust opacity and blur to ensure text readability with glassmorphism overlays.</li>
<li><strong>Interactive Canvas</strong>: Renders motion artwork (snowflakes, waves, constellations) inside a sandboxed canvas.</li>
</ul>
<blockquote>
<p>[!TIP]<br>Information on background configuration options and script templates can be found in the <strong><a href="./admin-design-editor.md">Design Editor Settings Introduction</a></strong> document.</p>
</blockquote>
<hr>
<h2 id="4-media-storage-support">💾 4. Media Storage Support</h2>
<p>You can switch the active cloud storage provider for uploading and serving media based on your needs.<br><figure data-align="left"><img src="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-en-004.webp" alt="img-admin-guide-admin-core-features-en-004" data-align="left" /></figure></p>
<ol>
<li>Navigate to the <strong><code>Storage Settings</code></strong> area at the bottom of the <strong><code>Media Library</code></strong> menu.</li>
<li>Select your storage provider and configure the credentials:<ul>
<li><strong>Cloudflare KV</strong>: Stores assets in edge KV storage for fast global delivery.</li>
<li><strong>Cloudflare R2</strong>: Cost-effective large object storage for managing mass images.</li>
<li><strong>Supabase Storage</strong>: Securely uploads and stores media assets in Supabase storage buckets.</li>
<li><strong>ImageKit.io</strong>: Connects global image CDN platforms for real-time optimization and compressed formatting.</li>
</ul>
</li>
<li><strong>Real-time Switch</strong>: Saving changes instantly shifts the active upload engine to the chosen storage provider.</li>
</ol>
<blockquote>
<p>[!NOTE]<br><strong>Limitations of default storage (Cloudflare KV) in media explorer</strong><br>Cloudflare KV does not support directory listing (read APIs) in the image explorer to minimize Workers usage and costs. Other external storages, such as Cloudflare R2, Supabase Storage, and ImageKit.io, fully support listing and previewing uploaded images.</p>
</blockquote>
]]></content:encoded>
            <category>Admin Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-core-features/desktop/img-admin-guide-admin-core-features-en-002.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Real-Time Design Editor and Background Customization Overview]]></title>
            <link>https://testblog-6br.pages.dev/en/admin-guide/admin-design-editor</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/admin-guide/admin-design-editor</guid>
            <pubDate>Wed, 15 Jul 2026 12:19:07 GMT</pubDate>
            <description><![CDATA[Instant real-time theme updates without rebuilds or redeployments.]]></description>
            <content:encoded><![CDATA[<h1 id="real-time-design-editor-and-background-customization-overview">🎨 Real-Time Design Editor and Background Customization Overview</h1>
<p>This document introduces the system that applies theme configurations in real time without requiring server rebuilds or redeployments, and outlines the 4 background settings: Solid, Gradient, Background Image, and Custom JavaScript Canvas.</p>
<hr>
<h2 id="1-real-time-design-editor-overview">🌌 1. Real-Time Design Editor Overview</h2>
<p>When you modify and save configuration values in the design editor, there is no need to rebuild or redeploy the blog server or CDN services. The database updates instantly, and the <strong>CSS variables and background rendering module reflect in the visitor&#39;s browser within seconds</strong>.<br>Before applying the design to the live blog, you can check the style changes through the preview screen inside the admin panel.</p>
<blockquote>
<p>[!WARNING]</p>
<h3 id="notice-differences-between-admin-preview-and-live-blog-design">⚠️ Notice: Differences Between Admin Preview and Live Blog Design</h3>
<p>The <strong>preview feature in the design editor does not guarantee a 100% identical environment</strong> to the live visitor page. Microscopic rendering differences (such as CSS variable evaluation or JavaScript Canvas rendering conditions) may occur between the admin preview and the actual blog.</p>
<p>Therefore, after changing any design configuration, we <strong>strongly recommend visiting your live blog directly and refreshing the page (F5)</strong> to verify the final rendering output.</p>
</blockquote>
<hr>
<h3 id="layout-amp-theme-configuration-settings">🧱 Layout &amp; Theme Configuration Settings</h3>
<h4 id="layout-customization">① Layout Customization</h4>
<ul>
<li><strong>Blog Structure</strong>: Select the base layout structure of the main page (e.g., 2-column, 3-column, etc.).</li>
<li><strong>Column Width Ratios</strong>: Fine-tune the width ratios of the main content column and sidebars (e.g., 1:2:1, 2:1:2, etc.).</li>
<li><strong>Maximum Width</strong>: Limit the maximum horizontal resolution of the entire layout container (e.g., 1200px, 1400px, etc.).</li>
<li><strong>Detailed Layout Values</strong>: Control the container side margins, card border radius, and box shadow depth effects.</li>
</ul>
<h4 id="basic-theme-colors">② Basic Theme Colors</h4>
<ul>
<li>Customize the Primary theme color, Secondary color, body Text color, Accent highlight color, Card Background (Card Bg), and Border colors.</li>
</ul>
<h4 id="typography-settings">③ Typography Settings</h4>
<ul>
<li>Apply web fonts by entering font family names from the Google Fonts directory, and set the base font size.</li>
</ul>
<h4 id="widget-arrangement-management">④ Widget Arrangement Management</h4>
<ul>
<li>Drag and drop available widgets to position them in different columns and arrange their render orders.</li>
</ul>
<hr>
<h2 id="2-device-specific-independent-widget-placement">🧱 2. Device-Specific Independent Widget Placement</h2>
<p>Configure widget positioning in the sidebar and content areas using drag-and-drop. You can customize layouts differently based on the visitor&#39;s screen size.</p>
<ul>
<li><strong>Desktop</strong>: Displays selected widgets (e.g., tag clouds, category trees) only on wider screens. For mobile access, elements are omitted at the HTML transmission stage to maintain fast loading times.</li>
<li><strong>Mobile</strong>: Hides the widget on desktop viewports and restricts exposure exclusively to smartphone-sized mobile resolutions.</li>
</ul>
<hr>
<h2 id="3-4-background-customization-options">🌈 3. 4 Background Customization Options</h2>
<p>Customize the sensory atmosphere of the blog using four supported background types:</p>
<h3 id="solid-solid-background">① Solid (Solid Background)</h3>
<ul>
<li>Choose a HEX code (e.g., <code>#3b82f6</code>) or HSL color code to create a clean, distraction-free screen that helps readers focus on the post content.</li>
</ul>
<h3 id="gradient-gradient-background">② Gradient (Gradient Background)</h3>
<ul>
<li>Create linear gradients where multiple colors blend smoothly using the <strong>Gradient Builder</strong> tool.</li>
<li>Adjust the gradient angle (Direction) slider and add or slide color stops to customize starting, intermediate, and ending colors.</li>
</ul>
<h3 id="image-background-image-amp-glassmorphism">③ Image (Background Image &amp; Glassmorphism)</h3>
<ul>
<li>Specify a remote image URL or use the upload button to add media assets to your library.</li>
<li><strong>Upload Optimization</strong>: Uploaded images are automatically converted to WebP format to prevent initial page load delays.</li>
<li><strong>Glassmorphism Overlay</strong>: Adjust the controls below to build a glassmorphism aesthetic that ensures text readability over background images.</li>
</ul>
<table>
<thead>
<tr>
<th align="left">Setting</th>
<th align="center">Recommended Range</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Glass Blur</strong></td>
<td align="center"><code>5px ~ 15px</code></td>
<td align="left">Applies a translucent frosted-glass blur filter below the content cards.</td>
</tr>
<tr>
<td align="left"><strong>Overlay Opacity</strong></td>
<td align="center"><code>10% ~ 30%</code></td>
<td align="left">Controls the opacity of the mask overlaid behind the card container.</td>
</tr>
<tr>
<td align="left"><strong>Overlay Color</strong></td>
<td align="center"><code>#000000</code> or <code>#ffffff</code></td>
<td align="left">Selects dark or light mask colors to maintain text contrast.</td>
</tr>
</tbody></table>
<h3 id="custom-javascript-custom-javascript-amp-canvas-background">④ Custom JavaScript (Custom JavaScript &amp; Canvas Background)</h3>
<ul>
<li>Supports custom JavaScript execution to render interactive Canvas animations on the client&#39;s browser background.</li>
<li>Provides direct rendering control over the background canvas element (<code>canvas id=&quot;bg-canvas&quot;</code>). Detailed security restrictions and power-saving policies are explained in <strong>[4. Custom JavaScript Integration Specifications]</strong> below.</li>
</ul>
<hr>
<h2 id="4-custom-javascript-integration-specifications">⚡ 4. Custom JavaScript Integration Specifications</h2>
<p>To prevent security risks and conserve mobile battery life, the following sandbox environment and battery-saving systems are applied.<br><figure data-align="center"><img src="https://sveltekitblog.com/images/posts/admin-design-editor/desktop/img-admin-guide-admin-design-editor-en-001.webp" alt="img-admin-guide-admin-design-editor-en-001" data-align="center" data-caption="Custom JS Canvas animation input window with security sandbox (Before activation)" /><figcaption>Custom JS Canvas animation input window with security sandbox (Before activation)</figcaption></figure><br><figure data-align="center"><img src="https://sveltekitblog.com/images/posts/admin-design-editor/desktop/img-admin-guide-admin-design-editor-en-002.webp" alt="img-admin-guide-admin-design-editor-en-002" data-align="center" data-caption="Custom JS Canvas animation input window with security sandbox (After activation)" /><figcaption>Custom JS Canvas animation input window with security sandbox (After activation)</figcaption></figure></p>
<h3 id="security-sandbox-and-csp">🔒 Security Sandbox and CSP</h3>
<p>The following security measures protect against malicious script injections:</p>
<ol>
<li><strong>Isolated Sandbox Structure</strong>: Canvas code runs inside an isolated <code>iframe</code> with limited execution privileges. Access to the parent page DOM, session cookies, or login credentials is blocked.</li>
<li><strong>Content Security Policy (CSP)</strong>: Outbound network calls and external script injections are completely blocked. Sensitive data cannot be leaked to external servers.</li>
<li><strong>JS API Restrictions</strong>: Disallowed APIs, including <code>fetch</code>, <code>XMLHttpRequest</code>, <code>WebSocket</code>, <code>eval</code>, <code>new Function</code>, <code>document.cookie</code>, and <code>localStorage</code>, are intercepted and commented out (<code>/* f_e_t_c_h (blocked) */</code>) by the validator script (<code>jsValidator.ts</code>).</li>
</ol>
<h3 id="power-and-performance-optimization">🔋 Power and Performance Optimization</h3>
<ul>
<li><strong>Out-of-View Auto-Pause</strong>: If a visitor scrolls and the background animation leaves the viewport, the render loop <strong>enters sleep mode</strong> to conserve CPU and GPU cycles. The animation resumes instantly when scrolled back into view.</li>
<li><strong>Mobile Frame Throttling</strong>: Background scripts are paused on mobile devices by default to prevent device overheating. Enabling the <strong>&quot;Run animation on mobile devices&quot;</strong> option scales down particle counts automatically to match mobile processing thresholds.</li>
</ul>
<hr>
<h2 id="5-sample-background-script-code-3-types">📝 5. Sample Background Script Code (3 Types)</h2>
<p>Choose <strong>[Custom JavaScript]</strong> in the background settings and copy one of the templates below into the code editor.</p>
<blockquote>
<p>[!NOTE]<br>Scripts must acquire the target canvas context using <code>document.getElementById(&#39;bg-canvas&#39;)</code>.</p>
</blockquote>
<h3 id="sample-a-winter-snowfall-snowfall">❄️ Sample A. Winter Snowfall (Snowfall)</h3>
<p>A background animation where snowflakes drift slowly down the screen.</p>
<pre><code class="language-javascript">(function() {
  const canvas = document.getElementById(&#39;bg-canvas&#39;);
  if (!canvas) return;
  const ctx = canvas.getContext(&#39;2d&#39;);
  
  let width = canvas.width = window.innerWidth;
  let height = canvas.height = window.innerHeight;
  
  // Mobile throttle detection
  const divisor = (window.bgConfig &amp;&amp; window.bgConfig.mobileThrottleDivisor) || 1;
  const maxSnowflakes = Math.floor(100 / divisor);
  const snowflakes = [];
  
  class Snowflake {
    constructor() {
      this.reset();
      this.y = Math.random() * height; // Initial random altitude
    }
    
    reset() {
      this.x = Math.random() * width;
      this.y = -10;
      this.radius = Math.random() * 3 + 1;
      this.speed = Math.random() * 1 + 0.5;
      this.opacity = Math.random() * 0.6 + 0.2;
    }
    
    update() {
      this.y += this.speed;
      // Gentle swaying motion
      this.x += Math.sin(this.y / 30) * 0.5;
      
      if (this.y &gt; height || this.x &lt; 0 || this.x &gt; width) {
        this.reset();
      }
    }
    
    draw() {
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
      ctx.fillStyle = `rgba(25, 25, 255, ${this.opacity})`;
      ctx.fill();
    }
  }
  
  // Particle instantiation
  for (let i = 0; i &lt; maxSnowflakes; i++) {
    snowflakes.push(new Snowflake());
  }
  
  function animate() {
    ctx.clearRect(0, 0, width, height);
    
    for (let i = 0; i &lt; snowflakes.length; i++) {
      snowflakes[i].update();
      snowflakes[i].draw();
    }
    requestAnimationFrame(animate);
  }
  
  // Resize handler
  window.addEventListener(&#39;resize&#39;, () =&gt; {
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;
  });
  
  animate();
})();
</code></pre>
<hr>
<h3 id="sample-b-constellation-network">🕸️ Sample B. Constellation Network</h3>
<p>An IT-inspired constellation pattern where floating node particles link with thin translucent lines when they drift close to one another.</p>
<pre><code class="language-javascript">(function() {
  const canvas = document.getElementById(&#39;bg-canvas&#39;);
  if (!canvas) return;
  const ctx = canvas.getContext(&#39;2d&#39;);
  
  let width = canvas.width = window.innerWidth;
  let height = canvas.height = window.innerHeight;
  
  const divisor = (window.bgConfig &amp;&amp; window.bgConfig.mobileThrottleDivisor) || 1;
  const particleCount = Math.floor(80 / divisor);
  const particles = [];
  const connectionDistance = 100;
  
  class Particle {
    constructor() {
      this.x = Math.random() * width;
      this.y = Math.random() * height;
      this.vx = (Math.random() - 0.5) * 0.8;
      this.vy = (Math.random() - 0.5) * 0.8;
      this.radius = Math.random() * 2 + 1.5;
    }
    
    update() {
      this.x += this.vx;
      this.y += this.vy;
      
      // Boundary collision
      if (this.x &lt; 0 || this.x &gt; width) this.vx *= -1;
      if (this.y &lt; 0 || this.y &gt; height) this.vy *= -1;
    }
    
    draw() {
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
      ctx.fillStyle = &#39;rgba(99, 102, 241, 0.4)&#39;; // Pastel indigo
      ctx.fill();
    }
  }
  
  for (let i = 0; i &lt; particleCount; i++) {
    particles.push(new Particle());
  }
  
  function drawLines() {
    for (let i = 0; i &lt; particles.length; i++) {
      for (let j = i + 1; j &lt; particles.length; j++) {
        const dx = particles[i].x - particles[j].x;
        const dy = particles[i].y - particles[j].y;
        const dist = Math.sqrt(dx * dx + dy * dy);
        
        if (dist &lt; connectionDistance) {
          const alpha = (connectionDistance - dist) / connectionDistance * 0.18;
          ctx.beginPath();
          ctx.moveTo(particles[i].x, particles[i].y);
          ctx.lineTo(particles[j].x, particles[j].y);
          ctx.strokeStyle = `rgba(99, 102, 241, ${alpha})`;
          ctx.lineWidth = 1;
          ctx.stroke();
        }
      }
    }
  }
  
  function animate() {
    ctx.clearRect(0, 0, width, height);
    for (let i = 0; i &lt; particles.length; i++) {
      particles[i].update();
      particles[i].draw();
    }
    drawLines();
    requestAnimationFrame(animate);
  }
  
  window.addEventListener(&#39;resize&#39;, () =&gt; {
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;
  });
  
  animate();
})();
</code></pre>
<hr>
<h3 id="sample-c-fluid-sine-waves">🌊 Sample C. Fluid Sine Waves</h3>
<p>A relaxing animation showing multiple overlapping pastel wave ripples flowing smoothly near the bottom of the screen.</p>
<pre><code class="language-javascript">(function() {
  const canvas = document.getElementById(&#39;bg-canvas&#39;);
  if (!canvas) return;
  const ctx = canvas.getContext(&#39;2d&#39;);
  
  let width = canvas.width = window.innerWidth;
  let height = canvas.height = window.innerHeight;
  
  let wave1 = {
    y: height * 0.85,
    length: 0.005,
    amplitude: 25,
    frequency: 0.012
  };
  
  let wave2 = {
    y: height * 0.88,
    length: 0.008,
    amplitude: 15,
    frequency: 0.022
  };
  
  let increment = 0;
  
  function animate() {
    ctx.clearRect(0, 0, width, height);
    
    // Draw background wave (translucent teal)
    ctx.beginPath();
    ctx.moveTo(0, height);
    for (let i = 0; i &lt; width; i++) {
      ctx.lineTo(i, wave1.y + Math.sin(i * wave1.length + increment) * wave1.amplitude);
    }
    ctx.lineTo(width, height);
    ctx.fillStyle = &#39;rgba(45, 212, 191, 0.1)&#39;;
    ctx.fill();
    
    // Draw foreground wave (translucent sky-blue)
    ctx.beginPath();
    ctx.moveTo(0, height);
    for (let i = 0; i &lt; width; i++) {
      ctx.lineTo(i, wave2.y + Math.sin(i * wave2.length - increment * 1.5) * wave2.amplitude);
    }
    ctx.lineTo(width, height);
    ctx.fillStyle = &#39;rgba(56, 189, 248, 0.15)&#39;;
    ctx.fill();
    
    // Adjust speed according to device configuration
    const speedFactor = (window.bgConfig &amp;&amp; window.bgConfig.mobileThrottleDivisor) ? 0.3 : 1;
    increment += wave1.frequency * speedFactor;
    
    requestAnimationFrame(animate);
  }
  
  window.addEventListener(&#39;resize&#39;, () =&gt; {
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;
    wave1.y = height * 0.85;
    wave2.y = height * 0.88;
  });
  
  animate();
})();
</code></pre>
<hr>
<h2 id="6-device-specific-mobile-background-configuration">📱 6. Device-Specific Mobile Background Configuration</h2>
<p>Customize your background settings depending on the user environment to optimize system performance.</p>
<ol>
<li><strong>Desktop Background</strong>: Choose <strong>Custom JavaScript</strong> on PC displays to run beautiful canvas animations.</li>
<li><strong>Enable Independent Mobile Settings</strong>: Check the <strong>&quot;Use different background for mobile devices&quot;</strong> toggle located at the bottom of the design editor.</li>
<li><strong>Mobile Optimization</strong>: Under the separate mobile configuration panel, select <strong>Solid</strong> or <strong>Gradient</strong> backgrounds to minimize processor workloads.</li>
<li><strong>Outcome</strong>: Conserves mobile batteries by running lightweight background styles on smartphones, while maintaining visually rich interactive motion rendering for desktop browsers.</li>
</ol>
]]></content:encoded>
            <category>Admin Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-design-editor/desktop/img-admin-guide-admin-design-editor-en-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Admin Frequently Asked Questions (FAQ) and Troubleshooting]]></title>
            <link>https://testblog-6br.pages.dev/en/admin-guide/admin-faq</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/admin-guide/admin-faq</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:57 GMT</pubDate>
            <description><![CDATA[Resolve installation and deployment errors, upgrade to the latest version, check mandatory image storage keys, and solve IP security blockages.]]></description>
            <content:encoded><![CDATA[<img src="https://sveltekitblog.com/images/posts/admin-faq/desktop/img-admin-guide-admin-faq-ko-001.webp" alt="img-admin-guide-admin-faq-ko-001" data-align="center"><h1 id="admin-frequently-asked-questions-faq-and-troubleshooting">❓ Admin Frequently Asked Questions (FAQ) and Troubleshooting</h1>
<p>This document provides solutions to common troubleshooting issues that may arise during the installation, deployment, and operation of the blog, as well as a guide to safely upgrading to the latest version.</p>
<hr>
<h2 id="q1-how-do-i-safely-upgrade-to-the-latest-version-or-restore-my-data">🗄️ Q1. How do I safely upgrade to the latest version or restore my data?</h2>
<p>To preserve database integrity and prevent unexpected build crashes, we strongly recommend following the safe upgrade procedures below.</p>
<h3 id="1-safe-upgrade-and-verification-procedure-recommended">1. Safe Upgrade and Verification Procedure (Recommended)</h3>
<p>To prevent database schema mismatches or template conflicts, do not deploy immediately to your production server. Instead, perform verification in a test environment first (applicable to non-Git users as well).</p>
<ol>
<li><strong>Preserve Settings Backup</strong>:<ul>
<li>Copy <code>wrangler.backup.json</code> and <code>.dev.vars</code> from your current development folder to a safe temporary location.</li>
<li><em>※ Note: The <code>.dev.vars</code> file contains critical credentials (passwords, OAuth keys, etc.) but is excluded from Git tracking (<code>.gitignore</code>) for security. Therefore, it is easily lost when updating or pulling source code. Be sure to back it up.</em></li>
</ul>
</li>
<li><strong>Back Up Production Data</strong>:<ul>
<li>Go to the <code>Content Backup</code> menu in your active blog admin panel and download the complete posts and settings backup file.</li>
</ul>
</li>
<li><strong>Clone/Download the New Version in an Isolated Folder</strong>:<ul>
<li>Do not overwrite the existing folder. Download or clone the latest release source code in an <strong>entirely new directory</strong>.</li>
</ul>
</li>
<li><strong>Deploy a Temporary Test Server</strong>:<ul>
<li>Navigate to the new folder, install dependencies (<code>npm install</code>), create a temporary database, and perform a test deployment.</li>
</ul>
</li>
<li><strong>Test Backup Restore</strong>:<ul>
<li>Access the newly deployed test admin panel and load the backup file downloaded in Step 2. Verify thoroughly that all posts and configurations display correctly without errors.</li>
</ul>
</li>
<li><strong>Apply Upgrade to Production Server</strong>:<ul>
<li>Only after validating that everything functions correctly on the test server, return to your original development folder, update the codebase (<code>git pull</code> or overwrite source), and run the individual app deployment commands (<code>npm run deploy:blog</code>, <code>npm run deploy:admin</code>) to complete the official upgrade.</li>
</ul>
</li>
</ol>
<h3 id="2-last-resort-for-data-loss">2. Last Resort for Data Loss</h3>
<ul>
<li><strong>Using the <code>npm run restore</code> Command</strong>:<ul>
<li>If the infrastructure settings or database are severely damaged and require a fresh restoration, use the backed-up <code>wrangler.backup.json</code> file as a <strong>last resort for data recovery</strong> to rebuild the entire infrastructure.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="q2-what-are-the-mandatory-keys-required-when-changing-image-storage-to-r2-supabase-or-imagekit">📂 Q2. What are the mandatory keys required when changing image storage to R2, Supabase, or ImageKit?</h2>
<p>Below is a checklist of mandatory environment variables and configurations required when using each external image storage provider.</p>
<table>
<thead>
<tr>
<th align="left">Storage Type</th>
<th align="left">Mandatory Input Items / Settings</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Cloudflare R2</strong></td>
<td align="left"><code>IMAGES</code> R2 Bucket Binding</td>
<td align="left">Must be mapped with the R2 bucket information inside <code>wrangler.json</code>.</td>
</tr>
<tr>
<td align="left"><strong>Supabase Storage</strong></td>
<td align="left"><code>supabase_storage_url</code><br><code>supabase_storage_key</code><br><code>supabase_storage_bucket</code></td>
<td align="left">API URL and Service Role Key must be accurate, and the bucket&#39;s access policy in Supabase Storage must be set to <strong>Public</strong> to prevent image rendering failures.</td>
</tr>
<tr>
<td align="left"><strong>ImageKit.io</strong></td>
<td align="left"><code>imagekit_url_endpoint</code><br><code>imagekit_public_key</code><br><code>imagekit_private_key</code></td>
<td align="left">Verify the endpoint URL format and ensure Cross-Origin Resource Sharing (CORS) is configured.</td>
</tr>
</tbody></table>
<hr>
<h2 id="q3-dashboard-analytics-charts-display-only-demo-data">📊 Q3. Dashboard analytics charts display only demo data.</h2>
<ul>
<li><strong>Cause</strong>: If Google Analytics 4 (GA4) API environment variables are missing or invalid, placeholder demo data is shown to prevent dashboard crashes.</li>
<li><strong>Setup</strong>: Add the following environment variables to the Cloudflare Pages settings and redeploy:<ul>
<li><code>GA4_PROPERTY_ID</code>: Google Analytics Property ID</li>
<li><code>GA4_CLIENT_EMAIL</code>: Google Cloud Service Account Email</li>
<li><code>GA4_PRIVATE_KEY</code>: Google Service Account Private Key</li>
</ul>
</li>
<li><strong>Caution</strong>: When writing <code>GA4_PRIVATE_KEY</code> in <code>.dev.vars</code>, ensure the entire key string is enclosed in double quotes (<code>&quot;</code>) so that line break codes (<code>\n</code>) are preserved correctly during parsing.</li>
</ul>
<hr>
<h2 id="q4-i-cannot-access-the-admin-page-or-some-data-sync-seems-to-be-missing-after-the-initial-setup-deployment">⚡ Q4. I cannot access the admin page, or some data sync seems to be missing after the initial setup deployment.</h2>
<ul>
<li><strong>Cause</strong>: Even if environment variable settings are successfully guided during the one-click setup, temporary network errors or system glitches may cause some encryption keys or secret variables (Secrets) to be partially missing or corrupted during deployment.</li>
<li><strong>Solution</strong>: Double-check the values in each app&#39;s <code>.dev.vars</code> file. Then, navigate to each folder and manually execute the deployment commands (<code>npm run deploy:blog</code> and <code>npm run deploy:admin</code>) <strong>once</strong>. The secret environment variables stored in your local <code>.dev.vars</code> will overwrite the environment variables on Cloudflare, resolving the issue.</li>
</ul>
<hr>
<h2 id="q5-an-quotforbidden-ip-not-allowedquot-or-403-forbidden-error-occurs-when-accessing-the-admin-page">🔒 Q5. An &quot;Forbidden (IP Not Allowed)&quot; or 403 Forbidden error occurs when accessing the admin page.</h2>
<p>Due to the security specifications of this blog, the deployment script automatically detects the public IP address of your deployment PC and injects it as the allowed IP (<code>ALLOWED_IP</code>) into the Pages Secret.</p>
<h3 id="admin-security-recommendation">⚠️ Admin Security Recommendation</h3>
<ul>
<li>To prevent account hijacking and unauthorized access, <strong>it is strongly recommended to restrict admin access and writing operations</strong> on public networks (e.g., cafes, libraries) or untrusted public PCs.</li>
</ul>
<h3 id="situation-specific-troubleshooting">💡 Situation-Specific Troubleshooting</h3>
<ul>
<li><strong>Situation A. Change in home or office IP address (e.g., router reboot)</strong>:<ul>
<li>Execute <code>npm run deploy:admin</code> once from your main development PC in the home/office. It will automatically detect the new public IP and redeploy, restoring your access immediately.</li>
</ul>
</li>
<li><strong>Situation B. Relocating development environment to post from an external location</strong>:<ul>
<li>Clone/download the project and execute a new setup deployment using your backed-up configuration file (<code>wrangler.backup.json</code>).</li>
<li><strong>※ Note:</strong> When returning to your original home/office, <strong>you must execute <code>npm run deploy:admin</code> again from your original development PC</strong> to restore the allowed IP back to your primary fixed IP.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="q6-better-auth-social-login-or-signup-errors-or-infinite-login-loops-occur">🔐 Q6. Better Auth (social login or signup) errors or infinite login loops occur.</h2>
<ul>
<li><strong>Cause</strong>: Session verification fails and loops if authentication-related secrets are missing or incorrect.</li>
<li><strong>Solution</strong>: <ol>
<li>Check <code>apps/blog/.dev.vars</code> and ensure that <code>BETTER_AUTH_SECRET</code> is set to a <strong>secure random string of at least 32 characters</strong>.</li>
<li>After correcting it, run <code>npm run deploy:blog</code> to overwrite and sync the secrets, which will restore normal authentication functionality.</li>
</ol>
</li>
</ul>
]]></content:encoded>
            <category>Admin Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/admin-faq/desktop/img-admin-guide-admin-faq-ko-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Blog Homepage Layout and Getting Started with Login]]></title>
            <link>https://testblog-6br.pages.dev/en/blog-guide/blog-getting-started</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/blog-guide/blog-getting-started</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:45 GMT</pubDate>
            <description><![CDATA[A brief introduction to the basic homepage layout and login/registration procedures of the blog app.]]></description>
            <content:encoded><![CDATA[<h1 id="blog-homepage-layout-and-getting-started-with-login">🌐 Blog Homepage Layout and Getting Started with Login</h1>
<p>This document provides a brief overview of the blog (<code>apps/blog</code>) homepage layout and basic login/registration methods.</p>
<hr>
<h2 id="1-blog-layout-and-navigation">🎨 1. Blog Layout and Navigation</h2>
<p>The blog layout is designed to help visitors locate information efficiently (Header, Sidebar, Main Content, and Footer).</p>
<ol>
<li><strong>Navigation Header (Header)</strong>:<ul>
<li><strong>Site Logo</strong>: Click to return to the homepage at any time.</li>
<li><strong>Shortcut Menu</strong>: Lists primary links configured by the administrator (e.g., categories, external channels).</li>
<li><strong>Language Selector</strong>: Click the globe icon to switch both menu languages and post translations instantly.</li>
</ul>
</li>
<li><strong>Sidebar</strong>:<ul>
<li>Displays category lists, the author profile card, and popular tags on desktop screens.</li>
<li>Automatically hidden on mobile resolutions to optimize readability and scrolling.</li>
</ul>
</li>
<li><strong>Main Content</strong>:<ul>
<li>Shows category filters and the latest published articles in card format.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="2-registration-and-login">🔑 2. Registration and Login</h2>
<p>Supports account registration and login for writing comments or guestbook entries.</p>
<h3 id="supported-login-and-sign-up-methods">⚙️ Supported Login and Sign-Up Methods</h3>
<ul>
<li><strong>Default Email Login/Sign-Up (Default)</strong>:<ul>
<li>Enabled by default post-installation. Users can sign up by providing an email address, display name (nickname), and password. Auto-login applies immediately upon successful registration.</li>
</ul>
</li>
</ul>
<figure data-align="center"><img src="https://sveltekitblog.com/images/posts/blog-getting-started/desktop/img-blog-guide-blog-getting-started-en-001.webp" alt="img-blog-guide-blog-getting-started-en-001" data-align="center" data-caption="Default email login screen" /><figcaption>Default email login screen</figcaption></figure><ul>
<li><strong>Social Login (Better-Auth)</strong>:<ul>
<li>Powered by the Better-Auth engine, supporting <strong>21 social login providers</strong> including Google, GitHub, Kakao, and Naver.</li>
<li>Social login buttons will only appear and function on the login screen after the administrator configures the respective client IDs and secrets as environment variables on the backend. (Integration details for social providers will be detailed in separate upcoming documents.)</li>
</ul>
</li>
</ul>
<figure data-align="center"><img src="https://sveltekitblog.com/images/posts/blog-getting-started/desktop/img-blog-guide-blog-getting-started-en-002.webp" alt="img-blog-guide-blog-getting-started-en-002" data-align="center" data-caption="Social + email login screen" /><figcaption>Social + email login screen</figcaption></figure><ul>
<li><strong>Login Status Display</strong>:<ul>
<li>Upon logging in, the top-right button switches to your profile image icon and a link to my-page.</li>
<li><em>Note: The user profile photo integration is currently in progress and may display a default profile icon instead.</em></li>
</ul>
</li>
</ul>
]]></content:encoded>
            <category>User Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/blog-getting-started/desktop/img-blog-guide-blog-getting-started-en-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Blog Core Features and Multi-Language Service Overview]]></title>
            <link>https://testblog-6br.pages.dev/en/blog-guide/blog-core-features</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/blog-guide/blog-core-features</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:37 GMT</pubDate>
            <description><![CDATA[An introduction to the blog's core features including real-time multi-language rendering and manual translation fallback, comment/reply hierarchies, and private guestbook posts.]]></description>
            <content:encoded><![CDATA[<h1 id="blog-core-features-and-multi-language-service-overview">🌐 Blog Core Features and Multi-Language Service Overview</h1>
<p>This document introduces core features of the blog, including viewing multi-language post translations and communicating via comments and the guestbook.</p>
<hr>
<h2 id="1-real-time-multi-language-body-switching-i18n">🌐 1. Real-Time Multi-Language Body Switching (i18n)</h2>
<p>This blog features a real-time multi-language viewer that updates not just basic UI labels but the actual post content itself to the selected language.</p>
<h3 id="how-it-works-amp-key-features">⚙️ How It Works &amp; Key Features</h3>
<ul>
<li><strong>Language-Specific URL Routing</strong>: Clicking the globe icon at the top or the language buttons near the post title directs the browser to the language-specific URL (e.g., prefixing with <code>/en</code> or <code>/ja</code>). In addition to default languages (Korean, English, Japanese), administrators can expand and publish in other languages by configuring the translation dictionary.</li>
</ul>
<img src="https://sveltekitblog.com/images/posts/blog-core-features/desktop/img-blog-guide-blog-core-features-ko-001.webp" alt="img-blog-guide-blog-core-features-ko-001" data-align="center" />
<img src="https://sveltekitblog.com/images/posts/blog-core-features/desktop/img-blog-guide-blog-core-features-ko-002.webp" alt="img-blog-guide-blog-core-features-ko-002" data-align="center" />
<figure data-align="center"><img src="https://sveltekitblog.com/images/posts/blog-core-features/desktop/img-blog-guide-blog-core-features-ko-003.webp" alt="img-blog-guide-blog-core-features-ko-003" data-align="center" data-caption="For posts published in multiple languages, clicking the corresponding language button immediately redirects you to the translated page." /><figcaption>For posts published in multiple languages, clicking the corresponding language button immediately redirects you to the translated page.</figcaption></figure><ul>
<li><strong>Manually Authored Data Loading (Not Machine Translation)</strong>: The system does not automatically machine-translate text in real time. Instead, it queries and loads the specific post data manually translated and saved by the author under each language tab (such as content translated using AI or translation services and stored in the database).</li>
<li><strong>Simultaneous Content &amp; Metadata Loading</strong>: More than simple text replacement, the database-stored title, excerpt, tags, and body HTML are completely swapped with the datasets of the selected language.</li>
<li><strong>Untranslated Post Fallback</strong>: If the author has not registered a translation for a specific language, the system shows a notice stating the translation is unavailable and falls back to rendering the default authoring language (e.g., Korean text) to ensure the reader&#39;s flow is uninterrupted.</li>
</ul>
<hr>
<h2 id="2-comment-and-nested-reply-hierarchy">💬 2. Comment and Nested Reply Hierarchy</h2>
<p>A clean comment section is placed below each article to facilitate discussion for both guest visitors and registered members.</p>
<ul>
<li><strong>Sharing Thoughts</strong>: Logged-in users can write and post comments to share ideas.</li>
<li><strong>Hierarchical Replies (Nested Comments)</strong>: Users can reply to a specific comment, organizing discussions in an easy-to-read tree structure.</li>
<li><strong>Security &amp; Integrity Preservation</strong>: Users can delete their own comments. However, if a comment with active replies is deleted, the system masks the content with the text &quot;This comment has been deleted&quot; to prevent breaking the overall thread hierarchy.</li>
</ul>
<hr>
<h2 id="3-guestbook-and-private-posts">📖 3. Guestbook and Private Posts</h2>
<p>Provides communication features through the blog guestbook page.</p>
<h3 id="private-guestbook-features">⚙️ Private Guestbook Features</h3>
<ul>
<li><strong>Writing Private Posts</strong>: Checking the <strong>[🔒 Private Post]</strong> checkbox hides the message from the general public.</li>
<li><strong>Exposure Restriction</strong>: Private guestbook posts are completely excluded from the lists of third-party visitors and logged-out users.</li>
<li><strong>Secure Communication</strong>: The post body is visible only to the author (when logged in) and the site administrator, ensuring private communication.</li>
</ul>
]]></content:encoded>
            <category>User Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/blog-core-features/desktop/img-blog-guide-blog-core-features-ko-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Reader Policy and Frequently Asked Questions (FAQ)]]></title>
            <link>https://testblog-6br.pages.dev/en/blog-guide/blog-faq</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/blog-guide/blog-faq</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:27 GMT</pubDate>
            <description><![CDATA[Introduces core principles regarding user data processing upon membership withdrawal and policies for service activity restrictions.]]></description>
            <content:encoded><![CDATA[<p><img src="https://sveltekitblog.com/images/posts/blog-faq/desktop/img-blog-guide-blog-faq-ko-001.webp" alt="img-blog-guide-blog-faq-ko-001"></p>
<h1 id="reader-policy-and-frequently-asked-questions-faq">❓ Reader Policy and Frequently Asked Questions (FAQ)</h1>
<p>This document introduces the basic principles of personal information and data processing upon account deletion, and the service activity restriction policy.</p>
<hr>
<h2 id="q1-what-happens-to-my-comments-and-guestbook-posts-when-i-delete-my-account">🚪 Q1. What happens to my comments and guestbook posts when I delete my account?</h2>
<p>The blog system applies an <strong>author anonymization policy</strong> to simultaneously protect the user&#39;s privacy and maintain the integrity of dialogue threads within the site.</p>
<h3 id="data-processing-principles-upon-deletion">💡 Data Processing Principles Upon Deletion</h3>
<ol>
<li><strong>Personal Identification Data Removal</strong>:<ul>
<li>When you delete your account, your active session and social login connection information are immediately and safely removed from the authentication system. However, for preventing abuse and security audits, minimal data containing the email and social ID connection information at the time of deletion is backed up in a segregated internal deletion log table (<code>deleted_users</code>). This log information is for internal administrative use only, and is completely anonymized and hidden on public pages to prevent any reverse-tracking of the author.</li>
</ul>
</li>
<li><strong>Content Data Retention</strong>:<ul>
<li>Your comments and guestbook messages remain on the screen to preserve the context of past discussions and conversations.</li>
</ul>
</li>
<li><strong>Nickname Anonymization</strong>:<ul>
<li>The link between your account and your posts is severed, and the author name automatically switches to <strong>&quot;Unknown&quot;</strong>. This prevents reverse-tracking of the actual author&#39;s identity.</li>
</ul>
</li>
</ol>
<ul>
<li><em>Note: If the administrator manually deletes your membership record entirely, your comments and guestbook entries may also be removed.</em></li>
</ul>
<hr>
<h2 id="q2-can-i-sign-up-again-immediately-after-deletion-or-withdrawal">🚪 Q2. Can I sign up again immediately after deletion or withdrawal?</h2>
<ul>
<li><strong>Voluntary Withdrawal</strong>:<ul>
<li>If you voluntarily delete your account, you can sign up again immediately using the same email or social account without any grace period.</li>
</ul>
</li>
<li><strong>Activity Ban (Ban) State</strong>:<ul>
<li>If your account is banned due to policy violations, you cannot sign up again with the same email because the email information still exists in the database.</li>
</ul>
</li>
<li><strong>Forced Deletion (Kick) State</strong>:<ul>
<li>If your account is forcibly deleted (Hard Deleted) by an administrator, the existing information is completely removed from the authentication tables, making it technically possible to sign up again with the same email immediately.</li>
<li>Although the email and social ID information from the time of the kick are preserved in the internal deletion log (<code>deleted_users</code>), there is currently no automated system block logic that compares this log at the time of registration to restrict sign-ups.</li>
<li>Therefore, to physically and completely prevent a malicious user from signing up again, the account should be kept in a <strong>&#39;Banned&#39;</strong> state rather than being forcibly deleted. (In a banned state, the email remains in the database, preventing re-registration due to the unique email constraint.)</li>
</ul>
</li>
</ul>
<hr>
<h2 id="q3-my-account-has-been-restricted-banned-or-forced-out-kicked">🚫 Q3. My account has been restricted (banned) or forced out (kicked).</h2>
<p>Checklist when your service usage is restricted (banned) or deleted (kicked) due to policy violations (such as posting spam or offensive content).</p>
<h3 id="core-checklist-for-activity-restrictions">💡 Core Checklist for Activity Restrictions</h3>
<ol>
<li><strong>Activity Ban (Ban)</strong>:<ul>
<li><strong>Status</strong>: Your login state remains active, but direct interactions such as posting comments, nested replies, or guestbook entries are temporarily or permanently restricted.</li>
<li><strong>Checking Reasons</strong>: When restricted, a notice saying <strong>&quot;Banned users cannot write comments.&quot;</strong> will appear in the comment form area, and you can view the specific reason and the expiration date of the restriction.</li>
</ul>
</li>
<li><strong>Forced Deletion (Kick/Delete)</strong>:<ul>
<li><strong>Status</strong>: The login account itself is completely deleted (Hard Deleted) from the database.</li>
<li><strong>Behavior</strong>: Once kicked, your active login session is immediately terminated. Subsequent login attempts will treat you as a new user with no registration history. Depending on the deletion behavior set by the administrator, comments you wrote may either be deleted entirely or anonymized as &#39;Unknown&#39;.</li>
</ul>
</li>
</ol>
]]></content:encoded>
            <category>User Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/blog-faq/desktop/img-blog-guide-blog-faq-ko-001.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Blog and Admin Feature Integration Overview]]></title>
            <link>https://testblog-6br.pages.dev/en/general-guide/general-integration-guide</link>
            <guid isPermaLink="true">https://testblog-6br.pages.dev/en/general-guide/general-integration-guide</guid>
            <pubDate>Wed, 15 Jul 2026 12:18:01 GMT</pubDate>
            <description><![CDATA[An integration document linking layout options, communication systems, multi-language publishing, and real-time design editors of the blog and admin console.]]></description>
            <content:encoded><![CDATA[<p><img src="https://sveltekitblog.com/images/posts/general-integration-guide/desktop/img-general-guide-general-integration-guide-ko-001.webp" alt="img-general-guide-general-integration-guide-ko-001"></p>
<h1 id="blog-and-admin-feature-integration-overview">📖 Blog and Admin Feature Integration Overview</h1>
<p>This document links individual feature overview pages to provide a structured map of the blog service and administration (admin) features.</p>
<p><em>Note: Detailed connection guidelines and technical manuals for each feature will be published individually in future posts.</em></p>
<hr>
<h2 id="feature-overview-index">📂 Feature Overview Index</h2>
<ul>
<li><strong>Admin Features</strong><ul>
<li><strong><a href="../admin-guide/admin-install-and-deploy">CMD One-Click Installation and Cloudflare Deployment Guide</a></strong>: Deploying the blog edge infrastructure to Cloudflare with a single terminal command.</li>
<li><strong><a href="../admin-guide/admin-getting-started">Admin First Access and Configuration Guide</a></strong>: Master password login, IP whitelist management, and multi-language UI dictionary configuration.</li>
<li><strong><a href="../admin-guide/admin-core-features">Core Admin Features and Dual Editors</a></strong>: Introduces Visual HTML and Markdown editors, batch-saving, and column layouts for desktop/mobile viewports.</li>
<li><strong><a href="../admin-guide/admin-design-editor">Real-Time Design Editor and Background Showcase</a></strong>: Four background modes (solid, gradient, image, canvas scripts) applied in real-time without rebuilds.</li>
<li><strong><a href="../admin-guide/admin-faq">Admin FAQ and Troubleshooting</a></strong>: Version upgrades, image storage key checks, post-deployment troubleshooting, and IP security unblock procedures.</li>
</ul>
</li>
<li><strong>Blog Features</strong><ul>
<li><strong><a href="../blog-guide/blog-getting-started">Blog Homepage Layout and Login</a></strong>: Highlights homepage widgets, multi-language UI switches, and login integrations.</li>
<li><strong><a href="../blog-guide/blog-core-features">Blog Core Features and Multi-Language Service</a></strong>: Real-time multi-language content switching, translation fallback logic, nested comment threads, and private guestbook communication.</li>
<li><strong><a href="../blog-guide/blog-faq">Reader Policy and FAQ</a></strong>: Data processing upon account deletion, re-registration policies, and ban/kick restriction details.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="core-process-of-blog-management">🚀 Core Process of Blog Management</h2>
<p>An overview of the setup and authoring workflow for blog administrators.</p>
<h3 id="1-ip-whitelisting-and-master-login">1. IP Whitelisting and Master Login</h3>
<ul>
<li>The admin panel requires the user&#39;s public IP address to be registered in the <code>ALLOWED_IP</code> whitelist before access is granted. (A 403 Forbidden page is shown if unregistered.)</li>
<li>Enter the master password on the secure login screen to establish your session.</li>
</ul>
<h3 id="2-multi-language-batch-saving">2. Multi-Language Batch Saving</h3>
<ul>
<li>Switch between the translation tabs in the writing editor to write content, and click the save button to publish or draft all languages simultaneously to the database.</li>
</ul>
<h3 id="3-real-time-theme-updates">3. Real-Time Theme Updates</h3>
<ul>
<li>Configure styles, colors, and interactive canvas backgrounds in the design editor. Saving changes propagates parameters as CSS variables in visitors&#39; browsers instantly, without redeployment.</li>
</ul>
<hr>
<h2 id="closing-remarks">💬 Closing Remarks</h2>
<p>The content covered in this document is a <strong>brief overview</strong> of the features offered by the blog and admin systems. In reality, there are many more detailed features and convenience options that we couldn&#39;t cover here, and discovering them on your own as you explore the system can be quite an enjoyable experience.</p>
<p>We plan to publish in-depth guides with detailed usage instructions and tips for each feature in future posts, so please feel free to visit again whenever you need them. If you have any questions or feedback, don&#39;t hesitate to leave a message via the guestbook or comments section at any time.</p>
]]></content:encoded>
            <category>General Guide</category>
            <enclosure url="https://sveltekitblog.com/images/posts/general-integration-guide/desktop/img-general-guide-general-integration-guide-ko-001.webp" length="0" type="image/webp"/>
        </item>
    </channel>
</rss>