Local-first personal blog platform — React Router v7, SQLite (FTS5), S3-compatible image storage.
- Framework: React Router v7 (Remix architecture, SSR)
- Server: Node.js + Express
- DB: better-sqlite3 (file-based, FTS5 search)
- Storage: S3-compatible (MinIO local / R2 / S3 / DO Spaces)
- Auth: Username/password (PBKDF2-SHA256)
- Editor: @uiw/react-md-editor + @uiw/react-markdown-preview
- Styling: Tailwind CSS v4
# 1) Install
cp .env.example .env
npm install
# 2) Start MinIO (S3-compatible storage)
npm run minio:up
# 3) Set MinIO bucket to public (run once)
docker exec myblog-minio mc alias set local http://localhost:9000 minioadmin minioadmin
docker exec myblog-minio mc anonymous set public local/myblog-images
# 4) Apply DB migrations
npm run db:migrate
# 5) Run dev server
npm run dev
# http://localhost:5173เมื่อรันครั้งแรก ระบบยังไม่มี admin account — ต้องตั้งค่าผ่าน setup wizard:
- เปิด http://localhost:5173/admin → redirect ไป
/setupอัตโนมัติ - กรอก Username และ Password (อย่างน้อย 8 ตัวอักษร)
- กด Create Admin Account
- ระบบ login ให้อัตโนมัติ → เข้าสู่
/admindashboard
ทำครั้งเดียว — หลังจากนี้ใช้
/loginเพื่อเข้าสู่ระบบปกติ
URL: http://localhost:5173/login
- กรอก Username + Password ที่ตั้งไว้ตอน setup
- หลัง login สำเร็จ → redirect ไป
/admin - Logout: คลิก avatar มุมขวาบน → Logout (หรือ POST
/auth/logout)
รันทั้ง myblog app + MinIO ด้วยคำสั่งเดียว ไม่ต้อง npm run dev แยก
# 1) Copy .env และแก้ SESSION_SECRET ให้ยาวอย่างน้อย 32 ตัวอักษร
cp .env.example .env
# 2) Build image + start ทุก service
docker compose up -d --build
# 3) ดู log
docker compose logs -f app
# 4) เปิด browser
# http://localhost:3000 ← myblog
# http://localhost:9001 ← MinIO ConsoleMinIO bucket
myblog-imagesจะถูกสร้างและตั้ง public policy อัตโนมัติผ่านminio-initservice
| Service | หน้าที่ |
|---|---|
minio |
S3-compatible storage |
minio-init |
สร้าง bucket + ตั้ง public policy (รันครั้งเดียว) |
app |
myblog Node.js SSR app |
docker compose up -d --build # build ใหม่ + start
docker compose up -d # start (ไม่ build ใหม่)
docker compose down # stop ทุก service
docker compose logs -f app # ดู log แบบ realtime
docker compose ps # ดู status ทุก serviceMinIO คือ S3-compatible object storage รันผ่าน Docker Compose ใช้เก็บรูปภาพที่ upload ผ่าน blog
| URL | ใช้สำหรับ |
|---|---|
| http://localhost:9000 | S3 API endpoint (ใช้ใน .env) |
| http://localhost:9001 | MinIO Console (Web UI) |
Credentials: minioadmin / minioadmin
npm run minio:up # start MinIO (รัน Docker container ชื่อ myblog-minio)
npm run minio:down # stop MinIOต้องรัน
npm run minio:upทุกครั้งที่เปิดเครื่องใหม่ก่อนใช้ฟีเจอร์ upload รูปค่ะ
หลัง npm run minio:up ครั้งแรก ต้องสร้าง bucket และตั้ง public policy:
วิธีที่ 1 — CLI (แนะนำ)
# 1) ลงทะเบียน alias ให้ mc client รู้จัก MinIO server
docker exec myblog-minio mc alias set local http://localhost:9000 minioadmin minioadmin
# 2) สร้าง bucket
docker exec myblog-minio mc mb local/myblog-images
# 3) ตั้ง policy เป็น public (ให้ทุกคนดูรูปได้โดยไม่ต้อง auth)
docker exec myblog-minio mc anonymous set public local/myblog-imagesวิธีที่ 2 — Web UI
- เปิด http://localhost:9001 → login
minioadmin/minioadmin - เมนูซ้าย → Administrator → Buckets → Create Bucket
- ตั้งชื่อ
myblog-images→ Create - คลิก bucket
myblog-images→ แท็บ Access Policy → เปลี่ยนเป็นpublic→ Save
# ดู bucket และ policy ที่ตั้งไว้
docker exec myblog-minio mc anonymous get local/myblog-images
# ควรได้: Access permission for `local/myblog-images` is `public`แก้ใน .env และ docker-compose.yml ให้ตรงกัน:
# .env
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET_ACCESS_KEY=your-secret-key
# docker-compose.yml
MINIO_ROOT_USER: your-access-key
MINIO_ROOT_PASSWORD: your-secret-keyDev (npm run dev) |
Production (npm start) |
|
|---|---|---|
| Port | 5173 | 3000 |
| Build ก่อน? | ไม่ต้อง | ต้องรัน npm run build |
| Hot reload | ✅ | ❌ |
npm run build
npm start
# http://localhost:3000Deploy: copy build/, migrations/, scripts/, package.json, server.mjs to server.
Set .env with production values (S3_* pointing to MinIO/R2/S3 of your choice), run npm ci --omit=dev, npm run db:migrate, then npm start (or PM2/systemd).
| Route | Purpose |
|---|---|
/ |
Home — published posts, search, categories |
/post/:slug |
Single post |
/:slug |
Category archive |
/about, /contact, /profile |
Static pages (markdown, edited via admin) |
/feed.xml |
RSS feed |
/setup |
First-run admin wizard |
/login |
Username/password login |
/admin |
Admin dashboard (posts, categories, pages, settings) |