blog-Quang-ng-

Quang Đông Japan

Blog tiếng Việt để chia sẻ trải nghiệm sống ở Nhật. Trang quản trị hỗ trợ tạo, chỉnh sửa, lưu nháp và xuất bản bài viết, kèm ảnh/video và thông tin blog.

Chạy trên máy cá nhân

Yêu cầu Python 3.9 trở lên.

cd quang-dong-japan-blog
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
flask --app app create-admin
python app.py

Mở http://127.0.0.1:8080. Lệnh create-admin sẽ hỏi tên đăng nhập và mật khẩu tối thiểu 12 ký tự. Tài khoản admin chỉ được tạo một lần; ứng dụng không cung cấp đăng ký công khai. Đăng nhập quản trị trực tiếp tại /admin/login; liên kết quản trị không hiển thị trên giao diện công khai.

Để thêm quản trị viên hoặc đặt lại mật khẩu, chạy python manage_admins.py, chọn thao tác a (thêm) hoặc r (đặt lại), rồi nhập tên đăng nhập và mật khẩu theo lời nhắc. Mật khẩu chỉ được lưu dưới dạng hash; không truyền mật khẩu trên dòng lệnh.

Nếu hosting không cấp Shell, có thể dùng biến môi trường: đặt BLOG_ADMIN_USERNAME và BLOG_ADMIN_PASSWORD trong phần Environment của dịch vụ rồi deploy/restart app. Sau khi khởi động, đăng nhập tại /admin/login. Giữ biến mật khẩu nếu filesystem/database của gói hosting không bền vững qua các lần restart; xóa biến này sau khi dùng chỉ khi database được lưu bền vững.

Trong VS Code, chọn interpreter .venv/bin/python (Python: Select Interpreter) để Pylance dùng đúng Flask/Werkzeug đã cài trong môi trường dự án.

Cấu hình

Sao chép .env.example thành .env rồi đặt các biến môi trường cần thiết. .env không được đưa lên Git:

Khách đăng ký tại biểu mẫu cuối trang chủ và phải bấm xác nhận trong email. Sau khi một bài mới được xuất bản (hoặc bản nháp chuyển sang công khai), hệ thống gửi email có tiêu đề, đường dẫn bài viết và đường dẫn hủy đăng ký. Chỉnh sửa một bài đã công khai không gửi lại thông báo. Dịch vụ SMTP cần được cấu hình trước khi email xác nhận hoặc thông báo có thể gửi đi.

Ứng dụng chấp nhận ảnh JPG, PNG, WEBP, GIF và video MP4, WEBM, MOV; mỗi lần request tối đa 60 MB và tối đa 12 tệp media mới cho một bài. Trong trình soạn thảo, đặt con trỏ tại vị trí cần chèn rồi bấm nút Ảnh hoặc Video để nhúng media ngay trong nội dung, hoặc dùng nút Ảnh URL, Video URL, Nhúng web để hiển thị media từ URL HTTPS bên ngoài mà không tải file đó về máy chủ. URL video tệp trực tiếp dùng thẻ video; URL trang web dùng iframe sandbox và cần website nguồn cho phép nhúng (một số nền tảng yêu cầu URL embed riêng hoặc chặn iframe). Ảnh chèn bằng URL có thể đặt mô tả alt/title và chú thích hiển thị bên dưới ảnh; chọn ảnh trong editor rồi bấm “Sửa tiêu đề/chú thích ảnh” để thay đổi. Ảnh tĩnh JPG, PNG và WEBP được tự xoay theo EXIF, thu nhỏ tối đa còn 1920 px mỗi chiều và nén sang WEBP chất lượng 82 trước khi lưu; nếu bản nén lớn hơn ảnh gốc thì ảnh gốc được giữ nguyên. GIF và ảnh động được giữ nguyên để không làm mất hoạt ảnh; video tải lên không bị chuyển đổi.

Triển khai VPS bằng Gunicorn

Tạo virtual environment, cài dependencies và tạo admin như phần chạy local. Sau đó cấu hình biến môi trường (hoặc nạp file env bằng service manager), trỏ BLOG_DATA_DIR tới thư mục dữ liệu bền vững mà user chạy ứng dụng có quyền ghi, và chạy:

gunicorn --workers 2 --bind 127.0.0.1:8080 app:app

Với Caddy, thêm site block riêng cho domain blog để Caddy tự cấp HTTPS:

zenligai.online {
    reverse_proxy 127.0.0.1:8080
}

Khối trên chỉ phục vụ domain gốc zenligai.online; giữ nguyên site block hiện có của callback.zenligai.online để bot tiếp tục hoạt động. Nếu dùng Nginx, cấu hình tương tự để proxy domain blog tới 127.0.0.1:8080. Không mở trực tiếp cổng ứng dụng ra Internet. Bật BLOG_COOKIE_SECURE=true khi HTTPS hoạt động. Nên chạy ứng dụng bằng user hệ thống riêng, hạn chế quyền ghi vào thư mục dữ liệu, và cấu hình tự khởi động/giám sát bằng systemd hoặc process manager hiện có.

Sao lưu và cập nhật

Mỗi khi tạo bài đã xuất bản, xuất bản bản nháp hoặc lưu chỉnh sửa bài đã xuất bản, ứng dụng tự tạo một file nén gồm bản SQLite nhất quán, uploads/ và .secret-key nếu khóa được tạo từ file. Mặc định file nằm trong BLOG_DATA_DIR/backups/; có thể đặt BLOG_BACKUP_DIR để dùng thư mục khác mà user chạy ứng dụng có quyền ghi. Ứng dụng chỉ giữ ba bản mới nhất và tự xóa bản cũ hơn. Nếu backup lỗi, bài viết vẫn được lưu; trang quản trị hiển thị cảnh báo và lỗi được ghi vào log service.

Để tạo bản backup ban đầu hoặc tạo thủ công:

flask --app app backup-now

Khi khôi phục, dừng service trước, giải nén một file backup-*.tar.gz, rồi chép blog.sqlite3, uploads/ và .secret-key (nếu có) về thư mục BLOG_DATA_DIR đúng vị trí, sau đó khởi động lại service. Nếu dùng BLOG_SECRET_KEY thay cho .secret-key, cần giữ nguyên giá trị biến môi trường đó. Backup trên cùng VPS không bảo vệ dữ liệu nếu VPS hoặc ổ đĩa bị mất; nên tải định kỳ các file backup sang một nơi lưu trữ khác.

Khi cập nhật code, giữ nguyên BLOG_DATA_DIR, database, uploads và secret key. Chỉ cập nhật dependencies theo requirements.txt, sau đó restart service. Không xóa thư mục dữ liệu khi deploy phiên bản mới.