1f1123acf8d70edf569199639d6b0b98c2f56258
The slug regex stripped everything that is not a-z0-9, so Chinese or punctuation-only titles produced an empty slug (stored as ""), which broke the unique index and article URLs. generateSlug now keeps ASCII word characters and returns "" when the title yields no URL-safe ASCII (e.g. CJK or punctuation only). Both the create and update handlers fall back to a "post-<id>" slug in that case; on create a temporary token-based placeholder is refined to post-<id> once the row exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Go Blog
A simple, fast blog engine built with Go, Gin, and Tailwind CSS. Supports SQLite and MySQL, with automatic first-run setup.
Features
- Multi-language — 中文 / English, auto-detected or manually switched
- OS-aware config — Linux
/etc/blog_go/, Windows./win/etc/blog_go/ - First-run setup — auto-creates config, database, and admin user
- Avatar + Profile — upload avatar, edit personal info, change password
- Role system — admin sees management panel; authors see profile only
- Responsive UI — Tailwind CSS, works on desktop and mobile
Tech Stack
| Layer | Library |
|---|---|
| Router | gin |
| ORM | gorm |
| SQLite | glebarez/sqlite (pure Go, no CGO) |
| Sessions | gin-contrib/sessions |
| Config | gopkg.in/yaml.v3 |
| Passwords | golang.org/x/crypto |
| CSS | Tailwind CSS (CDN) |
Quick Start
go run .
Open http://localhost:8080 and log in with admin / admin.
Configuration
On first run, a config file is created automatically:
| Platform | Config Path | Storage Path |
|---|---|---|
| Linux | /etc/blog_go/config.yaml |
/srv/blog_go/ |
| Windows | ./win/etc/blog_go/config.yaml |
./win/srv/blog_go/ |
# config.yaml
database:
type: sqlite # sqlite (default) or mysql
dsn: "" # MySQL DSN, ignored for sqlite
port: "8080" # web server port
path: ./win/srv/blog_go # storage path (db, uploads)
secret: <auto-generated> # session encryption key
MySQL
Edit config.yaml:
database:
type: mysql
dsn: user:password@tcp(127.0.0.1:3306)/blog_go?charset=utf8mb4&parseTime=True&loc=Local
port: "8080"
Create the database first:
CREATE DATABASE blog_go CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Project Structure
go_blog/
├── main.go # Entry point, routes, middleware wiring
├── config/
│ └── config.go # OS-aware config load / auto-create
├── models/
│ ├── user.go # User model + bcrypt
│ └── db.go # GORM init, migration, seed
├── middleware/
│ └── auth.go # Auth guard, language detection
├── handlers/
│ ├── helpers.go # DefaultData + getTr utilities
│ ├── home.go # GET /
│ ├── auth.go # GET/POST /login, POST /logout
│ ├── admin.go # GET /admin (protected)
│ └── profile.go # GET/POST /profile (protected)
├── i18n/
│ └── i18n.go # EN/ZH translation maps + Accept-Language
├── templates/
│ ├── layouts/base.html # Header (nav + avatar dropdown) + footer
│ ├── pages/
│ │ ├── home.html # Public home page
│ │ ├── login.html # Login form
│ │ └── profile.html # Edit profile page
│ └── admin/
│ └── dashboard.html # Admin dashboard
└── win/ # Auto-created runtime data (Windows)
├── etc/blog_go/
│ └── config.yaml
└── srv/blog_go/
├── blog.db
└── avatars/
Routes
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | / |
No | Home page |
| GET | /login |
No | Login form |
| POST | /login |
No | Submit login |
| POST | /logout |
No | Clear session |
| GET | /profile |
Yes | Edit profile |
| POST | /profile |
Yes | Save profile |
| GET | /admin |
Yes | Admin dashboard (admin only) |
| GET | /uploads/* |
No | Static files (avatars, etc.) |
User Roles
| Role | Profile | Admin Dashboard |
|---|---|---|
admin |
✓ | ✓ |
author |
✓ | ✗ |
License
MIT
Languages
Go
53.3%
HTML
45.9%
Shell
0.8%