kevinandClaude Fable 5 1f1123acf8 fix: generate non-empty slugs for non-ASCII titles
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>
2026-06-21 21:14:23 +08:00
up
2026-06-21 20:01:20 +08:00
up
2026-06-21 18:35:12 +08:00
up
2026-06-21 18:35:12 +08:00

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

S
Description
No description provided
Readme
543 KiB
Languages
Go 53.3%
HTML 45.9%
Shell 0.8%