Blog của tôi // Lưu trữ kỹ thuật số

Suy nghĩ, dự án và ghi chú kỹ thuật

Ban đầu, thiết lập blog của tôi được lên kế hoạch như một dự án thuần IPv6 thông qua WireGuard, vì toàn bộ hệ thống được vận hành trên một máy chủ tại gia (nhân tiện, bạn có thể nhận địa chỉ IPv6 miễn phí tại route64.org). Để tăng khả năng truy cập, hiện tại tôi đã bổ sung thêm một proxy IPv4 bên ngoài (Cảm ơn @Larvitz).

Tuy nhiên, ngay lập tức đã nảy sinh các vấn đề về SSL: Vì ban đầu cả bản ghi A và AAAA đều chạy qua proxy, nên việc xác thực Let's Encrypt trên máy chủ của tôi đã thất bại.

Giải pháp: „IPv6-Hack“

Giải pháp là trỏ trực tiếp bản ghi AAAA vào IP WireGuard của máy chủ thay vì dẫn nó qua proxy.

  • Tên miền: blog.burningboard.org
  • Bản ghi A (Proxy): 194.28.98.217
  • Bản ghi AAAA (Máy chủ): 2a11:6c7:f05:a8::2 (WireGuard)

Thông qua bản ghi AAAA trực tiếp này tới IP WireGuard, Let’s Encrypt vẫn có thể kết nối trực tiếp với máy chủ của tôi qua IPv6 (vì bản ghi AAAA được ưu tiên theo mặc định) và cấp chứng chỉ SSL. Lưu lượng IPv4 sẽ được proxy mã hóa và chuyển tiếp đến tôi.

Cấu hình cuối cùng

Để quá trình giao tiếp diễn ra suôn sẻ, chúng tôi đã phải điều chỉnh máy chủ Caddy:

1. Trên máy chủ của tôi (NixOS, blog.nix)

Để IP thực của khách truy cập được gửi đến chính xác và không bị ghi đè bởi IP của proxy, proxy này cần được đánh dấu là đáng tin cậy:

services.caddy.globalConfig = ''
  servers {
      trusted_proxies static 2a06:9801:1c:1000::10
  }
'';

2. Trên proxy bên ngoài (Caddy)

Để proxy có thể kết nối chính xác với máy chủ của tôi qua HTTPS, nó phải gửi kèm tên máy chủ (SNI):

reverse_proxy [https://[2a11:6c7:f05:a8::2]:443](https://[2a11:6c7:f05:a8::2]:443) {
    header_up Host {host}
    transport http {
        tls_server_name blog.burningboard.org
    }
}

Giờ đây blog đã có thể truy cập qua cả IPv4 và IPv6, được mã hóa an toàn và IP nhà riêng của tôi vẫn được giữ kín! 🚀

Điều quan trọng nhất trước tiên: Các tệp Markdown quen thuộc vẫn là nền tảng – đơn giản vì tôi là một fan lớn của giải pháp không cầu kỳ này. Nhưng bên dưới hệ thống, đã có rất nhiều thay đổi:

Tôi đã tinh chỉnh một vài thứ trong phần thiết lập:

📂 Các tệp MD: Cấu trúc blog vẫn đơn giản dựa trên nền tảng Markdown.

🌍 Toàn cầu hơn bao giờ hết: Blog của tôi hiện hỗ trợ dịch sang 43 ngôn ngữ. Vâng, bao gồm cả tiếng Klingon! 🖖 (Qapla'!)

Dự định ban đầu là một hệ thống dịch thời gian thực hoàn toàn tự động dựa trên nhận diện ngôn ngữ của trình duyệt. Tiết lộ nội dung (Spoiler): Nó chỉ hoạt động được một phần. Có thể thấy rằng: AI rất ấn tượng, nhưng vẫn chưa hoàn toàn đạt đến mức mà chúng ta mong muốn.

Giải pháp: Bây giờ tôi chỉ đơn giản là dịch trước mọi bài viết sang tất cả các ngôn ngữ đã định sẵn, điều này cũng tốt hơn nhiều cho các công cụ tìm kiếm (SEO). Nếu tính năng tự động nhận diện không hoạt động, bạn có thể chọn thủ công ngôn ngữ ưa thích của mình qua biểu tượng quả địa cầu, sau đó ngôn ngữ này sẽ được lưu lại một cách đơn giản thông qua cookie.

Các bản dịch hiện được thực hiện bằng Gemini 3 Flash, mang lại kết quả tốt đến kinh ngạc. Tuy nhiên, người ta nên giám sát AI thật kỹ: Trong lần chạy hàng loạt đầu tiên, các thẻ (tags) cũng bị dịch nhầm, điều mà tất nhiên là không nằm trong kế hoạch.

Mã nguồn vẫn có sẵn (nếu quan tâm, bạn cứ nhắn tin cho tôi) 👍 Nhưng lưu ý rằng hệ thống hiện cần một mã khóa API Gemini (Gemini API Key) 🔑 riêng.

Tôi đã nhanh chóng chuyển đổi blog của mình từ WriteFreely sang một hệ thống tự phát triển: MD-Blog (tất nhiên MD là viết tắt của Markdown). Nguyên nhân bắt nguồn từ một bản cập nhật bị lỗi của hệ thống cũ – nhưng cuối cùng, đó lại là động lực hoàn hảo để đơn giản hóa mọi thứ một cách triệt để và giành được toàn quyền kiểm soát thiết kế.

Cốt lõi của hệ thống là các tệp Markdown đơn giản trong thư mục data/, chúng được chuyển đổi thành HTML hiện đại trong quá trình chạy. Kết quả là tốc độ cực nhanh, không cần cơ sở dữ liệu và nhờ vào hệ thống thiết kế riêng (bao gồm cả chế độ tối - Dark Mode), giờ đây nó trông đúng như những gì tôi đã hình dung. Thậm chí, một nút chia sẻ Mastodon hiện đại cũng đã được tích hợp sẵn.

Nếu bạn quan tâm đến mã nguồn hoặc thiết lập tinh gọn này, hãy liên hệ với tôi qua Mastodon nhé!

Về cơ bản, ý tưởng đằng sau #Winboat rất tuyệt vời, nhưng việc triển khai hiện tại dường như vẫn còn hơi thiếu ổn định. Kể từ khi cài đặt vào đầu năm nay, hệ thống vẫn hoạt động bình thường, nhưng hôm nay phần mềm đã hoàn toàn ngừng hoạt động.

Image đột nhiên báo lỗi thiếu bộ nhớ RAM. Tôi đã cố gắng khắc phục sự cố theo cách thủ công, nhưng không may điều đó đã khiến hệ thống hoàn toàn không thể sử dụng được nữa. Thay vì tốn thêm thời gian để tìm lỗi, tôi đã chuyển thẳng sang Dockurr Windows-Image – dù sao thì đây cũng là nền tảng kỹ thuật của Winboat.

Thông báo lỗi

1. Chuẩn bị

Vì tôi đang sử dụng Podman, trước tiên tôi đã tạo các thư mục cần thiết trên hệ thống máy chủ (host) của mình. Điều này giúp đảm bảo tính toàn vẹn của dữ liệu trong trường hợp container cần được tạo lại:

mkdir -p $HOME/Windows/System
mkdir -p $HOME/Windows/Shared

2. Lệnh khởi chạy

Lưu ý quan trọng: Hãy thay thế các giá trị giữ chỗ trong các biến -e USERNAME-e PASSWORD bằng thông tin đăng nhập cá nhân của bạn.

podman run -d \
  --name windows \
  -p 8006:8006 \
  --device=/dev/kvm \
  --cap-add NET_ADMIN \
  -e RAM_SIZE="8G" \
  -e USERNAME="Carsten" \
  -e PASSWORD="1234" \
  -e LANGUAGE="German" \
  -v $HOME/Windows/System:/storage:Z \
  -v $HOME/Windows/Shared:/shared:Z \
  --stop-timeout 120 \
  dockurr/windows

Ngay khi container hoạt động, bạn có thể truy cập phiên bản Windows trực tiếp thông qua trình duyệt của mình:

http://127.0.0.1:8006

Container đang chạy

3. Tổng kết

Tôi chỉ cần thực hiện lệnh trên một lần duy nhất. Trong quá trình vận hành hàng ngày, môi trường Windows hiện có thể được điều khiển rất thuận tiện thông qua các lệnh tắt sau:

  • Khởi động: podman start windows
  • Dừng: podman stop windows (hoặc tắt máy trực tiếp bên trong Windows)
  • Kiểm tra trạng thái: podman ps -a

Các liên kết tham khảo:

Tôi vừa cài đặt một blog cá nhân — chủ yếu là để tìm hiểu kỹ hơn về #NixOS. Thật ngạc nhiên là mọi thứ diễn ra khá suôn sẻ và không hề phức tạp.

WriteFreely cực kỳ phù hợp cho mục đích này: tối giản, thiết lập nhanh và không có nhiều tính năng thừa thãi. Nó hoàn hảo để bắt đầu ngay lập tức và học hỏi thêm điều gì đó trong quá trình thực hiện. Cấu hình của nó cũng rất rõ ràng. Chỉ cần thiết lập vài tùy chọn, chuẩn bị thư mục, đặt một Reverse Proxy phía trước — thế là xong.

Dưới đây là cấu hình NixOS hiện tại của tôi cho việc này:

{ config, pkgs, ... }:

{
  services.writefreely = {
    enable = true;
    host = "blog.burningboard.org"; 
    settings = {
      server = {
        port = 8080;
        min_log_level = "debug";
      };
      app = {
        host = "https://blog.burningboard.org";
        single_user = true;
        landing = "/read";
        wf_modesty = true;
        federation = true;
        public_stats = true;
        theme = "write";
      };
    };
    stateDir = "/opt/writefreely";
  };

  # Sửa lỗi tạo khóa ActivityPub: Liên đoàn (Federation) yêu cầu openssl
  systemd.services.writefreely.path = [ pkgs.openssl ];

  # Tự động tạo thư mục dữ liệu với các quyền hạn chính xác
  systemd.tmpfiles.rules = [
    "d /opt/writefreely 0700 writefreely writefreely -"
  ];

  services.caddy.virtualHosts."blog.burningboard.org".extraConfig = ''
    reverse_proxy 127.0.0.1:8080 {
      header_up Host {host}
      header_up X-Real-IP {remote_host}
      header_up X-Forwarded-For {remote_host}
      header_up X-Forwarded-Proto {scheme}
    }
  '';
}

Về cơ bản chỉ có vậy thôi. NixOS thực sự giúp việc cấu hình các dịch vụ như thế này trở nên sạch sẽ và dễ dàng tái lập.