Skip to content

Kinh nghiệm thực chiến: Tích hợp Keystatic CMS & Hệ thống Quảng Cáo Động vào Astro

Trong phiên làm việc cực kỳ năng suất ngày hôm nay, Antigravity và User đã phối hợp xây dựng thành công 100% hệ thống lõi cho dự án Cú Đêm Blog. Bài viết này lưu lại toàn bộ lịch sử, tính năng và các “chiêu” fix bug cực hay để rút kinh nghiệm về sau.

Hệ thống đã biến một template Astro thô cứng thành một Cỗ Máy In Tiền linh hoạt với các tính năng sau:

  • Thiết lập thành công Keystatic chạy ở local mode (lúc dev) và github mode (lúc build production).
  • Gom toàn bộ Site Settings, SEO Defaults, Social Links vào Singletons để quản trị tập trung. Không còn hard-code trong file .astro nữa.
  • Tạo Collection Blog hoàn chỉnh với đầy đủ Frontmatter: Title, Slug tự xâu, SEO Meta, Categories, Tags và hệ thống kiểm duyệt bản nháp.

💰 Hệ thống Quảng Cáo “Thông Minh” bọc lót toàn Site

Section titled “💰 Hệ thống Quảng Cáo “Thông Minh” bọc lót toàn Site”
  • Ad Placements (Global): Quản lý 6 vị trí quảng cáo (Top Banner, Below Title, Inside Content 1/2, Sidebar Sticky, Below Content, Sticky Bottom). Nhập ID thẻ tuỳ chọn ngay trên CMS.
  • Tiêu diệt Khoảng trắng chết: Xử lý triệt để bài toán “AdSense không hiển thị ở Dev Mode hoặc bị AdBlock” bằng CSS thông minh:
    .ad-container:empty { display: none !important; }
    .ad-container:empty + span { display: none !important; }
  • Ad Placeholder: Cố tình giữ lại khung giả lập [AdSense Test Banner] ở môi trường Dev (dựa vào import.meta.env.DEV) để Team dễ dàng test UI/Responsive mà không bị mù mờ vị trí.
  • Micro Quản Lý (Per-Post Ad Control): Ở mỗi bài viết, tác giả có quyền tích Checkbox để tắt quảng cáo Sidebar, tắt quảng cáo trong nội dung hoặc tắt toàn bộ quảng cáo của bài viết đó (dành cho bài nhạy cảm/nhà tài trợ riêng).

📢 Affiliate Banner Chủ Động (Top Announcement)

Section titled “📢 Affiliate Banner Chủ Động (Top Announcement)”

Ý tưởng xuất thần của User: Tạo một thanh Banner mỏng dính sát mép trên màn hình toàn Website để chạy các chiến dịch (Ví dụ: Siêu Sale Shopee 8/3).

  • Cấu hình màu nền gradient cực gắt (bg-gradient-to-r).
  • Gắn nút bấm chứa link Tracking Affiliate.
  • Bật/tắt 1 nốt nhạc bằng Checkbox trong Admin mà không cần sửa code. Có nút đóng banner tắt bằng JS gọn gàng.

2. Các Lỗi “Sập Site” đã gặp và Bài học Xương Máu

Section titled “2. Các Lỗi “Sập Site” đã gặp và Bài học Xương Máu”

❌ Lỗi 1: 404 Not Found trang chi tiết bài viết

Section titled “❌ Lỗi 1: 404 Not Found trang chi tiết bài viết”
  • Nguyên nhân: Các bài viết được tạo ra từ Keystatic là định dạng .mdoc (Markdoc) hoặc lưu dưới dạng data source riêng. Astro mặc định sẽ không tự sinh ra link nếu không có “đường ống” xử lý.
  • Cách khắc phục: Tạo Route động src/pages/blog/[slug].astro. Dùng hàm getStaticPaths() để vét toàn bộ bài trong CMS kết hợp Component <DocumentRenderer> của Keystatic chuyển thể Node Tree thành HTML.

❌ Lỗi 2: Expected ”]” but found ”}” (Sập mọi URL layout)

Section titled “❌ Lỗi 2: Expected ”]” but found ”}” (Sập mọi URL layout)”
  • Nguyên nhân: Chỉ vì một thao tác dư phím, để lọt 1 dấu } vào mảng dữ liệu Local Test trong PostLayout.astro làm phá vỡ cấu trúc cú pháp. Astro không thể Build CSS/JS dẫn đến chết toàn trang.
  • Kinh nghiệm: Khi Copy/Paste hoặc sửa file Layout siêu to khổng lồ (>2000 dòng), phải cực kỳ cẩn thận với mảng/object thuần tuý. Debug bằng cách soi ngay dòng báo lỗi trên Terminal.

❌ Lỗi 3: Cannot read properties of undefined (reading ‘entry’) ở Dev Mode

Section titled “❌ Lỗi 3: Cannot read properties of undefined (reading ‘entry’) ở Dev Mode”
  • Bối cảnh: System bật cấu hình SSR (output: 'server'), nhưng trang [slug].astro lại được render lai Hybrid.
  • Nguyên nhân: Cơ chế SSR của Vite thi thoảng ném mất Astro.props.post (prop không được truyền từ getStaticPaths sang) khiến biến post.entry bị undefined và gây vỡ trang khi gọi tiếp frontmatter.
  • Giải pháp: Viết Fallback Logic thần thánh. Cứ hễ mất Props, ta dùng Slug để tự Read lại từ Server!
    const { slug } = Astro.params;
    let { post } = Astro.props; // Khai báo let
    // Tự cứu thương khi props tàng hình
    if (!post && slug) {
    const entry = await reader.collections.blog.read(slug);
    if (entry) post = { slug, entry };
    }
    // Tránh chết site cứng ngắc
    if (!post) return Astro.redirect('/404');
  • Bài học phụ: Đừng quên thêm cờ export const prerender = true; vào đầu file động [slug].astro để yêu cầu Astro tạo ra tĩnh HTML từ Keystatic thay vì bắt Server làm việc mỗi lần request.

Sự ăn ý giữa “Não Bộ” (Antigravity làm kỹ thuật, code cứng, debug lỗi) và “Tầm Nhìn” (User đề xuất Affiliate Banner chủ động, yêu cầu giữ khung Ads QC, thiết lập vị trí chuyển đổi) đã rút ngắn quy trình Development tính bằng ngày xuống chỉ còn vài giờ đồng hồ.

Status Workflow: Keystatic backend ổn định, Layout siêu sạch sẽ. Sẵn sàng Deploy lên Github và Cloudflare Pages.