up
This commit is contained in:
@@ -1,41 +1,50 @@
|
||||
# Go Blog
|
||||
|
||||
A simple, fast blog engine built with Go, Gin, and Tailwind CSS. Supports SQLite and MySQL, with automatic first-run setup.
|
||||
一个简洁、快速的博客引擎,使用 Go + Gin + Tailwind CSS 构建。支持 SQLite 和 MySQL,首次运行自动完成配置。
|
||||
|
||||
## 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
|
||||
- **文章管理** — 文章的创建、编辑、删除,支持标签分类、自定义发布时间和更新时间
|
||||
- **评论系统** — 文章评论功能,后台可审核(通过/拒绝/删除),支持评论设置
|
||||
- **用户系统** — 用户注册、登录,角色分为管理员(admin)和作者(author)
|
||||
- **角色权限** — 管理员可访问后台管理面板;作者可管理自己的文章
|
||||
- **个人中心** — 上传头像(支持裁剪)、编辑个人信息、修改密码
|
||||
- **站点设置** — 自定义站点标题、副标题、favicon 图标
|
||||
- **导航管理** — 自定义首页导航链接,支持图标
|
||||
- **瀑布流布局** — 首页文章以瀑布流展示,支持无限滚动加载
|
||||
- **阅读统计** — 文章阅读量统计,带机器人流量检测
|
||||
- **附件上传** — 文章支持上传附件,基于内容寻址自动去重
|
||||
- **RSS 订阅** — 自动生成 RSS Feed(`/rss`、`/feed`)
|
||||
- **搜索功能** — 文章全文搜索
|
||||
- **多语言** — 支持中文 / English,自动检测浏览器语言或手动切换
|
||||
- **自适应界面** — Tailwind CSS,桌面端和移动端均可正常使用
|
||||
- **开箱即用** — 首次运行自动创建配置文件、数据库和管理员账号
|
||||
|
||||
## Tech Stack
|
||||
## 技术栈
|
||||
|
||||
| Layer | Library |
|
||||
| 层级 | 库/工具 |
|
||||
|---|---|
|
||||
| Router | [gin](https://github.com/gin-gonic/gin) |
|
||||
| 路由 | [gin](https://github.com/gin-gonic/gin) |
|
||||
| ORM | [gorm](https://gorm.io) |
|
||||
| SQLite | [glebarez/sqlite](https://github.com/glebarez/sqlite) (pure Go, no CGO) |
|
||||
| Sessions | [gin-contrib/sessions](https://github.com/gin-contrib/sessions) |
|
||||
| Config | [gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) |
|
||||
| Passwords | [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto/bcrypt) |
|
||||
| CSS | [Tailwind CSS](https://tailwindcss.com) (CDN) |
|
||||
| SQLite | [glebarez/sqlite](https://github.com/glebarez/sqlite)(纯 Go,无需 CGO) |
|
||||
| Session | [gin-contrib/sessions](https://github.com/gin-contrib/sessions) |
|
||||
| 配置 | [gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) |
|
||||
| 密码 | [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto/bcrypt) |
|
||||
| CSS | [Tailwind CSS](https://tailwindcss.com)(CDN) |
|
||||
|
||||
## Quick Start
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
go run .
|
||||
```
|
||||
|
||||
Open http://localhost:8080 and log in with **admin** / **admin**.
|
||||
打开 <http://localhost:8080> ,使用 **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/` |
|
||||
@@ -43,16 +52,16 @@ On first run, a config file is created automatically:
|
||||
```yaml
|
||||
# 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
|
||||
type: sqlite # sqlite(默认)或 mysql
|
||||
dsn: "" # MySQL 连接串,sqlite 模式下忽略
|
||||
port: "8080" # Web 服务端口
|
||||
path: ./win/srv/blog_go # 数据存储路径(数据库、上传文件)
|
||||
secret: <自动生成> # Session 加密密钥
|
||||
```
|
||||
|
||||
### MySQL
|
||||
### 使用 MySQL
|
||||
|
||||
Edit `config.yaml`:
|
||||
编辑 `config.yaml`:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
@@ -61,41 +70,82 @@ database:
|
||||
port: "8080"
|
||||
```
|
||||
|
||||
Create the database first:
|
||||
先创建数据库:
|
||||
|
||||
```sql
|
||||
CREATE DATABASE blog_go CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
go_blog/
|
||||
├── main.go # Entry point, routes, middleware wiring
|
||||
├── main.go # 入口,路由注册,中间件装配
|
||||
├── config/
|
||||
│ └── config.go # OS-aware config load / auto-create
|
||||
│ └── config.go # 配置加载 / 自动创建(区分操作系统)
|
||||
├── models/
|
||||
│ ├── user.go # User model + bcrypt
|
||||
│ └── db.go # GORM init, migration, seed
|
||||
│ ├── user.go # 用户模型 + bcrypt
|
||||
│ ├── article.go # 文章模型
|
||||
│ ├── article_tag.go # 文章-标签关联
|
||||
│ ├── article_view.go # 文章阅读统计
|
||||
│ ├── attachment.go # 附件模型
|
||||
│ ├── bot_detector.go # 机器人流量检测
|
||||
│ ├── comment.go # 评论模型
|
||||
│ ├── comment_config.go # 评论配置
|
||||
│ ├── config_cache.go # 平台配置缓存
|
||||
│ ├── db.go # GORM 初始化、自动迁移
|
||||
│ ├── nav_link.go # 导航链接模型
|
||||
│ ├── seed.go # 初始数据填充
|
||||
│ ├── site_setting.go # 站点设置模型
|
||||
│ ├── tag.go # 标签模型
|
||||
│ └── upload_config.go # 上传配置
|
||||
├── middleware/
|
||||
│ └── auth.go # Auth guard, language detection
|
||||
│ └── auth.go # 登录验证、角色鉴权、语言检测
|
||||
├── 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)
|
||||
│ ├── helpers.go # 公共工具函数
|
||||
│ ├── home.go # 首页
|
||||
│ ├── article.go # 文章详情、文章列表 API
|
||||
│ ├── auth.go # 登录
|
||||
│ ├── admin.go # 管理后台首页
|
||||
│ ├── admin_comment.go # 评论管理
|
||||
│ ├── admin_user.go # 用户管理
|
||||
│ ├── admin_analytics.go # 阅读统计
|
||||
│ ├── attachment.go # 附件上传/删除
|
||||
│ ├── comment.go # 评论提交
|
||||
│ ├── my_articles.go # 用户文章管理
|
||||
│ ├── profile.go # 个人中心
|
||||
│ ├── rss.go # RSS Feed
|
||||
│ ├── settings.go # 站点/导航/上传/评论设置
|
||||
│ └── upload_validator.go # 上传文件校验
|
||||
├── i18n/
|
||||
│ └── i18n.go # EN/ZH translation maps + Accept-Language
|
||||
│ └── i18n.go # 中英文翻译映射 + Accept-Language 检测
|
||||
├── templates/
|
||||
│ ├── layouts/base.html # Header (nav + avatar dropdown) + footer
|
||||
│ ├── layouts/base.html # 公共布局(导航栏 + 头像下拉菜单 + 页脚)
|
||||
│ ├── 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)
|
||||
│ │ ├── home.html # 首页(瀑布流 + 无限滚动)
|
||||
│ │ ├── article.html # 文章详情页
|
||||
│ │ ├── login.html # 登录页
|
||||
│ │ ├── register.html # 注册页
|
||||
│ │ ├── profile.html # 个人中心
|
||||
│ │ ├── search.html # 搜索页
|
||||
│ │ └── 404.html # 404 页面
|
||||
│ ├── admin/
|
||||
│ │ ├── dashboard.html # 管理后台首页
|
||||
│ │ ├── article_list.html # 文章列表
|
||||
│ │ ├── article_create.html# 文章编辑
|
||||
│ │ ├── comment_list.html # 评论审核
|
||||
│ │ ├── user_list.html # 用户列表
|
||||
│ │ ├── user_form.html # 用户表单
|
||||
│ │ ├── analytics_views.html # 阅读统计
|
||||
│ │ ├── settings_site.html # 站点设置
|
||||
│ │ ├── settings_navlinks.html # 导航链接设置
|
||||
│ │ ├── settings_upload.html # 上传设置
|
||||
│ │ ├── settings_download.html # 下载设置
|
||||
│ │ └── settings_comment.html # 评论设置
|
||||
│ └── user/
|
||||
│ ├── my_articles.html # 我的文章列表
|
||||
│ └── my_article_form.html # 我的文章编辑
|
||||
└── win/ # 运行时数据目录(Windows,自动创建)
|
||||
├── etc/blog_go/
|
||||
│ └── config.yaml
|
||||
└── srv/blog_go/
|
||||
@@ -103,26 +153,73 @@ go_blog/
|
||||
└── 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` | ✓ | ✗ |
|
||||
| GET | `/` | 首页 |
|
||||
| GET | `/search` | 搜索页 |
|
||||
| GET | `/api/articles` | 文章列表 API(无限滚动) |
|
||||
| GET | `/rss`、`/feed` | RSS 订阅 |
|
||||
| GET | `/article/:slug` | 文章详情 |
|
||||
| POST | `/article/:slug/comments` | 发表评论 |
|
||||
| GET | `/login` | 登录页 |
|
||||
| POST | `/login` | 提交登录 |
|
||||
| GET | `/register` | 注册页 |
|
||||
| POST | `/register` | 提交注册 |
|
||||
| POST | `/logout` | 退出登录 |
|
||||
| GET | `/uploads/*` | 静态文件(头像、附件等) |
|
||||
|
||||
## License
|
||||
### 管理后台(需管理员权限)
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|---|---|---|
|
||||
| GET | `/admin` | 管理后台首页 |
|
||||
| GET | `/admin/articles` | 文章列表 |
|
||||
| GET/POST | `/admin/articles/new` | 新建文章 |
|
||||
| GET/POST | `/admin/articles/:id/edit` | 编辑文章 |
|
||||
| POST | `/admin/articles/:id/delete` | 删除文章 |
|
||||
| POST | `/admin/articles/attachments` | 上传附件 |
|
||||
| POST | `/admin/articles/attachments/:id/delete` | 删除附件 |
|
||||
| GET | `/admin/articles/:id/attachments` | 附件列表 |
|
||||
| GET | `/admin/comments` | 评论管理 |
|
||||
| POST | `/admin/comments/:id/approve` | 通过评论 |
|
||||
| POST | `/admin/comments/:id/reject` | 拒绝评论 |
|
||||
| POST | `/admin/comments/:id/delete` | 删除评论 |
|
||||
| GET | `/admin/users` | 用户列表 |
|
||||
| GET/POST | `/admin/users/new` | 新建用户 |
|
||||
| GET/POST | `/admin/users/:id/edit` | 编辑用户 |
|
||||
| POST | `/admin/users/:id/delete` | 删除用户 |
|
||||
| GET | `/admin/analytics/views` | 阅读统计 |
|
||||
| GET/POST | `/admin/settings/site` | 站点设置 |
|
||||
| GET/POST | `/admin/settings/navlinks` | 导航链接设置 |
|
||||
| GET/POST | `/admin/settings/upload` | 上传设置 |
|
||||
| GET/POST | `/admin/settings/download` | 下载设置 |
|
||||
| GET/POST | `/admin/settings/comments` | 评论设置 |
|
||||
|
||||
### 个人中心(需登录)
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|---|---|---|
|
||||
| GET/POST | `/profile` | 编辑个人信息 |
|
||||
| POST | `/profile/avatar` | 上传头像 |
|
||||
| GET | `/my/articles` | 我的文章列表 |
|
||||
| GET/POST | `/my/articles/new` | 新建文章 |
|
||||
| GET/POST | `/my/articles/:id/edit` | 编辑文章 |
|
||||
| POST | `/my/articles/:id/delete` | 删除文章 |
|
||||
| POST | `/my/articles/attachments` | 上传附件 |
|
||||
| POST | `/my/articles/attachments/:id/delete` | 删除附件 |
|
||||
| GET | `/my/articles/:id/attachments` | 附件列表 |
|
||||
|
||||
## 用户角色
|
||||
|
||||
| 角色 | 个人中心 | 我的文章 | 管理后台 |
|
||||
| --- | --- | --- | --- |
|
||||
| `admin` | ✓ | ✓ | ✓ |
|
||||
| `author` | ✓ | ✓ | ✗ |
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT
|
||||
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SERVICE_NAME="blog_go"
|
||||
SERVICE_USER="blog_go"
|
||||
CONFIG_DIR="/etc/${SERVICE_NAME}"
|
||||
DATA_DIR="/srv/${SERVICE_NAME}"
|
||||
INSTALL_DIR="/opt/${SERVICE_NAME}"
|
||||
BINARY_NAME="${SERVICE_NAME}"
|
||||
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo "请使用 root 权限运行: sudo $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "${SCRIPT_DIR}"
|
||||
|
||||
echo "拉取最新代码..."
|
||||
git pull
|
||||
|
||||
echo "编译 Go 程序..."
|
||||
go build -o "${BINARY_NAME}" .
|
||||
|
||||
echo "检查系统用户..."
|
||||
if ! id -u "${SERVICE_USER}" >/dev/null 2>&1; then
|
||||
useradd --system --home-dir "${DATA_DIR}" --shell /usr/sbin/nologin "${SERVICE_USER}"
|
||||
fi
|
||||
|
||||
echo "创建目录..."
|
||||
install -d -m 0750 -o "${SERVICE_USER}" -g "${SERVICE_USER}" "${CONFIG_DIR}" "${DATA_DIR}"
|
||||
install -d -m 0755 -o "${SERVICE_USER}" -g "${SERVICE_USER}" "${INSTALL_DIR}"
|
||||
|
||||
echo "安装程序和模板文件..."
|
||||
install -m 0755 -o root -g root "${SCRIPT_DIR}/${BINARY_NAME}" "${INSTALL_DIR}/${BINARY_NAME}"
|
||||
rm -rf "${INSTALL_DIR}/templates"
|
||||
cp -a "${SCRIPT_DIR}/templates" "${INSTALL_DIR}/templates"
|
||||
chown -R root:root "${INSTALL_DIR}/templates"
|
||||
chown "${SERVICE_USER}:${SERVICE_USER}" "${INSTALL_DIR}"
|
||||
chmod 0755 "${INSTALL_DIR}"
|
||||
find "${INSTALL_DIR}/templates" -type d -exec chmod 0755 {} \;
|
||||
find "${INSTALL_DIR}/templates" -type f -exec chmod 0644 {} \;
|
||||
|
||||
if [[ ! -f "${CONFIG_DIR}/config.yaml" ]]; then
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
cat > "${CONFIG_DIR}/config.yaml" <<EOF
|
||||
database:
|
||||
type: sqlite
|
||||
dsn: ""
|
||||
port: "8080"
|
||||
path: ${DATA_DIR}
|
||||
secret: ${SECRET}
|
||||
EOF
|
||||
chown "${SERVICE_USER}:${SERVICE_USER}" "${CONFIG_DIR}/config.yaml"
|
||||
chmod 0640 "${CONFIG_DIR}/config.yaml"
|
||||
fi
|
||||
|
||||
echo "写入 systemd 服务文件..."
|
||||
cat > "${SERVICE_FILE}" <<EOF
|
||||
[Unit]
|
||||
Description=Blog Go Service
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${SERVICE_USER}
|
||||
Group=${SERVICE_USER}
|
||||
WorkingDirectory=${INSTALL_DIR}
|
||||
ExecStart=${INSTALL_DIR}/${BINARY_NAME}
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
ReadWritePaths=${CONFIG_DIR} ${DATA_DIR} ${INSTALL_DIR}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable "${SERVICE_NAME}"
|
||||
systemctl restart "${SERVICE_NAME}"
|
||||
|
||||
echo "部署完成,服务状态:"
|
||||
systemctl --no-pager --full status "${SERVICE_NAME}"
|
||||
Reference in New Issue
Block a user