Showing preview only (554K chars total). Download the full file or copy to clipboard to get everything.
Repository: lexkong/apiserver_demos
Branch: master
Commit: 87e2a93052ef
Files: 417
Total size: 459.0 KB
Directory structure:
gitextract_0wqow4l0/
├── Makefile
├── README.md
├── a.go
├── demo01/
│ ├── README.md
│ ├── handler/
│ │ └── sd/
│ │ └── check.go
│ ├── main.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo02/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ └── sd/
│ │ └── check.go
│ ├── main.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo03/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ └── sd/
│ │ └── check.go
│ ├── main.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo04/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ └── sd/
│ │ └── check.go
│ ├── main.go
│ ├── model/
│ │ └── init.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo05/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ └── create.go
│ ├── main.go
│ ├── model/
│ │ └── init.go
│ ├── pkg/
│ │ └── errno/
│ │ ├── code.go
│ │ └── errno.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo06/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ └── init.go
│ ├── pkg/
│ │ └── errno/
│ │ ├── code.go
│ │ └── errno.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo07/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ └── errno/
│ │ ├── code.go
│ │ └── errno.go
│ ├── router/
│ │ ├── middleware/
│ │ │ └── header.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo08/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ └── errno/
│ │ ├── code.go
│ │ └── errno.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo09/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ └── token/
│ │ └── token.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo10/
│ ├── README.md
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ └── token/
│ │ └── token.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo11/
│ ├── Makefile
│ ├── README.md
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ └── token/
│ │ └── token.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo12/
│ ├── Makefile
│ ├── README.md
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo13/
│ ├── Makefile
│ ├── README.md
│ ├── admin.sh
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo14/
│ ├── Makefile
│ ├── README.md
│ ├── admin.sh
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo15/
│ ├── Makefile
│ ├── README.md
│ ├── admin.sh
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ ├── util.go
│ └── util_test.go
├── demo16/
│ ├── Makefile
│ ├── README.md
│ ├── admin.sh
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ ├── util.go
│ └── util_test.go
└── demo17/
├── Makefile
├── README.md
├── admin.sh
├── conf/
│ ├── config.yaml
│ ├── server.crt
│ └── server.key
├── config/
│ └── config.go
├── db.sql
├── docs/
│ ├── docs.go
│ └── swagger/
│ ├── swagger.json
│ └── swagger.yaml
├── handler/
│ ├── handler.go
│ ├── sd/
│ │ └── check.go
│ └── user/
│ ├── create.go
│ ├── delete.go
│ ├── get.go
│ ├── list.go
│ ├── login.go
│ ├── update.go
│ └── user.go
├── main.go
├── model/
│ ├── init.go
│ ├── model.go
│ └── user.go
├── pkg/
│ ├── auth/
│ │ └── auth.go
│ ├── constvar/
│ │ └── constvar.go
│ ├── errno/
│ │ ├── code.go
│ │ └── errno.go
│ ├── token/
│ │ └── token.go
│ └── version/
│ ├── base.go
│ ├── doc.go
│ └── version.go
├── router/
│ ├── middleware/
│ │ ├── auth.go
│ │ ├── header.go
│ │ ├── logging.go
│ │ └── requestid.go
│ └── router.go
├── service/
│ └── service.go
└── util/
├── util.go
└── util_test.go
================================================
FILE CONTENTS
================================================
================================================
FILE: Makefile
================================================
clean:
find . -name "[._]*.s[a-w][a-z]" | xargs -i rm -f {}
.PHONY: clean
================================================
FILE: README.md
================================================
## 目录
**注意:** 此项目不再维护,如果想学习本项目可以移步:https://github.com/marmotedu/goserver
**另外**:此课程已经升级为极客时间课程,课程介绍[《Go 语言项目开发实战》](https://time.geekbang.org/column/intro/100079601),建议学习该课程,更专业、内容更多。
## apiserver_demos 项目介绍
本教程是掘金小册:[基于 Go 语言构建企业级的 RESTful API 服务](https://juejin.cn/book/6844733730678898702) 实战类教学项目,旨在让初学者花尽可能短的时间,通过尽可能详细的步骤,历经 17 个 demo,最终一步步构建出一个生产级的 API 服务器。从开发准备到 API 设计,再到 API 实现、测试和部署,每一步都详细介绍了如何去构建。通过本教程的学习,你将学到如下知识点:

知识点很多,跟着教程一节一节进行学习,你将完整的学会如何用 Go 做 API 开发。
## 源码目录介绍
| 目录 | 介绍 |
| --- | --- |
| demo01 |实战:启动一个最简单的 RESTful API 服务器 |
| demo02 |实战:配置文件读取 |
| demo03 |实战:记录和管理 API 日志 |
| demo04 |实战:初始化 MySQL 数据库并建立连接 |
| demo05 |实战:自定义业务错误信息 |
| demo06 |实战:读取和返回 HTTP 请求 |
| demo07 |实战:用户业务逻辑处理(业务处理) |
| demo08 |实战:HTTP 调用添加自定义处理逻辑 |
| demo09 |实战:API 身份验证 |
| demo10 |进阶:用 HTTPS 加密 API 请求 |
| demo11 |进阶:用 Makefile 管理 API 项目 |
| demo12 |进阶:给 API 命令增加版本功能 |
| demo13 |进阶:给 API 增加启动脚本 |
| demo14 |进阶:基于 Nginx 的 API 部署方案 |
| demo15 |进阶:go test 测试你的代码 |
| demo16 |进阶:API 性能分析 |
| demo17 |进阶:生成 Swagger 在线文档 |
## 项目适宜人群
- 掌握一定 Go 语法基础,零 Go 服务器研发经验,想通过一个完整的实战,来系统学习 Go 服务器开发的同学;
- 有意从事 Go 服务器开发,但尚未入门或入门尚浅的同学;
- 有过 Go 服务器开发经验,但想了解某一部分构建方法的同学。
## 你应该具备什么
- 基本的 Go 语言编程知识
- 基本的 Linux/Uinx 命令行知识
================================================
FILE: a.go
================================================
================================================
FILE: demo01/README.md
================================================
实战:启动一个最简单的RESTful API服务器
================================================
FILE: demo01/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo01/main.go
================================================
package main
import (
"errors"
"log"
"net/http"
"time"
"apiserver/router"
"github.com/gin-gonic/gin"
)
func main() {
// Create the Gin engine.
g := gin.New()
middlewares := []gin.HandlerFunc{}
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middlewares...,
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Print("The router has been deployed successfully.")
}()
log.Printf("Start to listening the incoming requests on http address: %s", ":8080")
log.Printf(http.ListenAndServe(":8080", g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < 2; i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get("http://127.0.0.1:8080" + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Print("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo01/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo01/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo02/README.md
================================================
实战:配置文件读取
================================================
FILE: demo02/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
================================================
FILE: demo02/config/config.go
================================================
package config
import (
"log"
"strings"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Printf("Config file changed: %s", e.Name)
})
}
================================================
FILE: demo02/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo02/main.go
================================================
package main
import (
"errors"
"log"
"net/http"
"time"
"apiserver/config"
"apiserver/router"
"github.com/gin-gonic/gin"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
// init config
if err := config.Init(*cfg); err != nil {
panic(err)
}
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
// Create the Gin engine.
g := gin.New()
middlewares := []gin.HandlerFunc{}
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middlewares...,
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Print("The router has been deployed successfully.")
}()
log.Printf("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Printf(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < viper.GetInt("max_ping_count"); i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get(viper.GetString("url") + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Print("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo02/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo02/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo03/README.md
================================================
实战:记录和管理API日志
================================================
FILE: demo03/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
log:
writers: file,stdout
logger_level: DEBUG
logger_file: log/apiserver.log
log_format_text: false
rollingPolicy: size
log_rotate_date: 1
log_rotate_size: 1
log_backup_count: 7
================================================
FILE: demo03/config/config.go
================================================
package config
import (
"strings"
"github.com/fsnotify/fsnotify"
"github.com/lexkong/log"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 初始化日志包
c.initLog()
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
func (c *Config) initLog() {
passLagerCfg := log.PassLagerCfg{
Writers: viper.GetString("log.writers"),
LoggerLevel: viper.GetString("log.logger_level"),
LoggerFile: viper.GetString("log.logger_file"),
LogFormatText: viper.GetBool("log.log_format_text"),
RollingPolicy: viper.GetString("log.rollingPolicy"),
LogRotateDate: viper.GetInt("log.log_rotate_date"),
LogRotateSize: viper.GetInt("log.log_rotate_size"),
LogBackupCount: viper.GetInt("log.log_backup_count"),
}
log.InitWithConfig(&passLagerCfg)
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Infof("Config file changed: %s", e.Name)
})
}
================================================
FILE: demo03/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo03/main.go
================================================
package main
import (
"errors"
"net/http"
"time"
"apiserver/config"
"apiserver/router"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
// init config
if err := config.Init(*cfg); err != nil {
panic(err)
}
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
// Create the Gin engine.
g := gin.New()
middlewares := []gin.HandlerFunc{}
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middlewares...,
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Info("The router has been deployed successfully.")
}()
log.Infof("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Info(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < viper.GetInt("max_ping_count"); i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get(viper.GetString("url") + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Info("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo03/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo03/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo04/README.md
================================================
实战:初始化Mysql数据库并建立连接
================================================
FILE: demo04/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
log:
writers: file,stdout
logger_level: DEBUG
logger_file: log/apiserver.log
log_format_text: false
rollingPolicy: size
log_rotate_date: 1
log_rotate_size: 1
log_backup_count: 7
================================================
FILE: demo04/config/config.go
================================================
package config
import (
"strings"
"github.com/fsnotify/fsnotify"
"github.com/lexkong/log"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 初始化日志包
c.initLog()
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
func (c *Config) initLog() {
passLagerCfg := log.PassLagerCfg{
Writers: viper.GetString("log.writers"),
LoggerLevel: viper.GetString("log.logger_level"),
LoggerFile: viper.GetString("log.logger_file"),
LogFormatText: viper.GetBool("log.log_format_text"),
RollingPolicy: viper.GetString("log.rollingPolicy"),
LogRotateDate: viper.GetInt("log.log_rotate_date"),
LogRotateSize: viper.GetInt("log.log_rotate_size"),
LogBackupCount: viper.GetInt("log.log_backup_count"),
}
log.InitWithConfig(&passLagerCfg)
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Infof("Config file changed: %s", e.Name)
})
}
================================================
FILE: demo04/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo04/main.go
================================================
package main
import (
"errors"
"net/http"
"time"
"apiserver/config"
"apiserver/model"
"apiserver/router"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
// init config
if err := config.Init(*cfg); err != nil {
panic(err)
}
// init db
model.DB.Init()
defer model.DB.Close()
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
// Create the Gin engine.
g := gin.New()
middlewares := []gin.HandlerFunc{}
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middlewares...,
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Info("The router has been deployed successfully.")
}()
log.Infof("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Info(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < viper.GetInt("max_ping_count"); i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get(viper.GetString("url") + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Info("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo04/model/init.go
================================================
package model
import (
"fmt"
"github.com/lexkong/log"
"github.com/spf13/viper"
// MySQL driver.
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type Database struct {
Self *gorm.DB
Docker *gorm.DB
}
var DB *Database
func openDB(username, password, addr, name string) *gorm.DB {
config := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
username,
password,
addr,
name,
true,
//"Asia/Shanghai"),
"Local")
db, err := gorm.Open("mysql", config)
if err != nil {
log.Errorf(err, "Database connection failed. Database name: %s", name)
}
// set for db connection
setupDB(db)
return db
}
func setupDB(db *gorm.DB) {
db.LogMode(viper.GetBool("gormlog"))
//db.DB().SetMaxOpenConns(20000) // 用于设置最大打开的连接数,默认值为0表示不限制.设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。
db.DB().SetMaxIdleConns(0) // 用于设置闲置的连接数.设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。
}
// used for cli
func InitSelfDB() *gorm.DB {
return openDB(viper.GetString("db.username"),
viper.GetString("db.password"),
viper.GetString("db.addr"),
viper.GetString("db.name"))
}
func GetSelfDB() *gorm.DB {
return InitSelfDB()
}
func InitDockerDB() *gorm.DB {
return openDB(viper.GetString("docker_db.username"),
viper.GetString("docker_db.password"),
viper.GetString("docker_db.addr"),
viper.GetString("docker_db.name"))
}
func GetDockerDB() *gorm.DB {
return InitDockerDB()
}
func (db *Database) Init() {
DB = &Database{
Self: GetSelfDB(),
Docker: GetDockerDB(),
}
}
func (db *Database) Close() {
DB.Self.Close()
DB.Docker.Close()
}
================================================
FILE: demo04/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo04/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo05/README.md
================================================
实战:自定义业务错误信息
================================================
FILE: demo05/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
log:
writers: file,stdout
logger_level: DEBUG
logger_file: log/apiserver.log
log_format_text: false
rollingPolicy: size
log_rotate_date: 1
log_rotate_size: 1
log_backup_count: 7
================================================
FILE: demo05/config/config.go
================================================
package config
import (
"strings"
"github.com/fsnotify/fsnotify"
"github.com/lexkong/log"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 初始化日志包
c.initLog()
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
func (c *Config) initLog() {
passLagerCfg := log.PassLagerCfg{
Writers: viper.GetString("log.writers"),
LoggerLevel: viper.GetString("log.logger_level"),
LoggerFile: viper.GetString("log.logger_file"),
LogFormatText: viper.GetBool("log.log_format_text"),
RollingPolicy: viper.GetString("log.rollingPolicy"),
LogRotateDate: viper.GetInt("log.log_rotate_date"),
LogRotateSize: viper.GetInt("log.log_rotate_size"),
LogBackupCount: viper.GetInt("log.log_backup_count"),
}
log.InitWithConfig(&passLagerCfg)
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Infof("Config file changed: %s", e.Name)
})
}
================================================
FILE: demo05/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo05/handler/user/create.go
================================================
package user
import (
"fmt"
"net/http"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
)
// Create creates a new user account.
func Create(c *gin.Context) {
var r struct {
Username string `json:"username"`
Password string `json:"password"`
}
var err error
if err := c.Bind(&r); err != nil {
c.JSON(http.StatusOK, gin.H{"error": errno.ErrBind})
return
}
log.Debugf("username is: [%s], password is [%s]", r.Username, r.Password)
if r.Username == "" {
err = errno.New(errno.ErrUserNotFound, fmt.Errorf("username can not found in db: xx.xx.xx.xx")).Add("This is add message.")
log.Errorf(err, "Get an error")
}
if errno.IsErrUserNotFound(err) {
log.Debug("err type is ErrUserNotFound")
}
if r.Password == "" {
err = fmt.Errorf("password is empty")
}
code, message := errno.DecodeErr(err)
c.JSON(http.StatusOK, gin.H{"code": code, "message": message})
}
================================================
FILE: demo05/main.go
================================================
package main
import (
"errors"
"net/http"
"time"
"apiserver/config"
"apiserver/model"
"apiserver/router"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
// init config
if err := config.Init(*cfg); err != nil {
panic(err)
}
// init db
model.DB.Init()
defer model.DB.Close()
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
// Create the Gin engine.
g := gin.New()
middlewares := []gin.HandlerFunc{}
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middlewares...,
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Info("The router has been deployed successfully.")
}()
log.Infof("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Info(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < viper.GetInt("max_ping_count"); i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get(viper.GetString("url") + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Info("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo05/model/init.go
================================================
package model
import (
"fmt"
"github.com/lexkong/log"
"github.com/spf13/viper"
// MySQL driver.
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type Database struct {
Self *gorm.DB
Docker *gorm.DB
}
var DB *Database
func openDB(username, password, addr, name string) *gorm.DB {
config := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
username,
password,
addr,
name,
true,
//"Asia/Shanghai"),
"Local")
db, err := gorm.Open("mysql", config)
if err != nil {
log.Errorf(err, "Database connection failed. Database name: %s", name)
}
// set for db connection
setupDB(db)
return db
}
func setupDB(db *gorm.DB) {
db.LogMode(viper.GetBool("gormlog"))
//db.DB().SetMaxOpenConns(20000) // 用于设置最大打开的连接数,默认值为0表示不限制.设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。
db.DB().SetMaxIdleConns(0) // 用于设置闲置的连接数.设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。
}
// used for cli
func InitSelfDB() *gorm.DB {
return openDB(viper.GetString("db.username"),
viper.GetString("db.password"),
viper.GetString("db.addr"),
viper.GetString("db.name"))
}
func GetSelfDB() *gorm.DB {
return InitSelfDB()
}
func InitDockerDB() *gorm.DB {
return openDB(viper.GetString("docker_db.username"),
viper.GetString("docker_db.password"),
viper.GetString("docker_db.addr"),
viper.GetString("docker_db.name"))
}
func GetDockerDB() *gorm.DB {
return InitDockerDB()
}
func (db *Database) Init() {
DB = &Database{
Self: GetSelfDB(),
Docker: GetDockerDB(),
}
}
func (db *Database) Close() {
DB.Self.Close()
DB.Docker.Close()
}
================================================
FILE: demo05/pkg/errno/code.go
================================================
package errno
var (
// Common errors
OK = &Errno{Code: 0, Message: "OK"}
InternalServerError = &Errno{Code: 10001, Message: "Internal server error."}
ErrBind = &Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct."}
// user errors
ErrUserNotFound = &Errno{Code: 20102, Message: "The user was not found."}
)
================================================
FILE: demo05/pkg/errno/errno.go
================================================
package errno
import "fmt"
type Errno struct {
Code int
Message string
}
func (err Errno) Error() string {
return err.Message
}
// Err represents an error
type Err struct {
Code int
Message string
Err error
}
func New(errno *Errno, err error) *Err {
return &Err{Code: errno.Code, Message: errno.Message, Err: err}
}
func (err *Err) Add(message string) error {
//err.Message = fmt.Sprintf("%s %s", err.Message, message)
err.Message += " " + message
return err
}
func (err *Err) Addf(format string, args ...interface{}) error {
//return err.Message = fmt.Sprintf("%s %s", err.Message, fmt.Sprintf(format, args...))
err.Message += " " + fmt.Sprintf(format, args...)
return err
}
func (err *Err) Error() string {
return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err)
}
func IsErrUserNotFound(err error) bool {
code, _ := DecodeErr(err)
return code == ErrUserNotFound.Code
}
func DecodeErr(err error) (int, string) {
if err == nil {
return OK.Code, OK.Message
}
switch typed := err.(type) {
case *Err:
return typed.Code, typed.Message
case *Errno:
return typed.Code, typed.Message
default:
}
return InternalServerError.Code, err.Error()
}
================================================
FILE: demo05/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo05/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/handler/user"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
u := g.Group("/v1/user")
{
u.POST("", user.Create)
}
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo06/README.md
================================================
实战:读取和返回HTTP请求
================================================
FILE: demo06/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
log:
writers: file,stdout
logger_level: DEBUG
logger_file: log/apiserver.log
log_format_text: false
rollingPolicy: size
log_rotate_date: 1
log_rotate_size: 1
log_backup_count: 7
================================================
FILE: demo06/config/config.go
================================================
package config
import (
"strings"
"github.com/fsnotify/fsnotify"
"github.com/lexkong/log"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 初始化日志包
c.initLog()
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
func (c *Config) initLog() {
passLagerCfg := log.PassLagerCfg{
Writers: viper.GetString("log.writers"),
LoggerLevel: viper.GetString("log.logger_level"),
LoggerFile: viper.GetString("log.logger_file"),
LogFormatText: viper.GetBool("log.log_format_text"),
RollingPolicy: viper.GetString("log.rollingPolicy"),
LogRotateDate: viper.GetInt("log.log_rotate_date"),
LogRotateSize: viper.GetInt("log.log_rotate_size"),
LogBackupCount: viper.GetInt("log.log_backup_count"),
}
log.InitWithConfig(&passLagerCfg)
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Infof("Config file changed: %s", e.Name)
})
}
================================================
FILE: demo06/handler/handler.go
================================================
package handler
import (
"net/http"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func SendResponse(c *gin.Context, err error, data interface{}) {
code, message := errno.DecodeErr(err)
// always return http.StatusOK
c.JSON(http.StatusOK, Response{
Code: code,
Message: message,
Data: data,
})
}
================================================
FILE: demo06/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo06/handler/user/create.go
================================================
package user
import (
"fmt"
. "apiserver/handler"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
)
// Create creates a new user account.
func Create(c *gin.Context) {
var r CreateRequest
if err := c.Bind(&r); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
admin2 := c.Param("username")
log.Infof("URL username: %s", admin2)
desc := c.Query("desc")
log.Infof("URL key param desc: %s", desc)
contentType := c.GetHeader("Content-Type")
log.Infof("Header Content-Type: %s", contentType)
log.Debugf("username is: [%s], password is [%s]", r.Username, r.Password)
if r.Username == "" {
SendResponse(c, errno.New(errno.ErrUserNotFound, fmt.Errorf("username can not found in db: xx.xx.xx.xx")), nil)
return
}
if r.Password == "" {
SendResponse(c, fmt.Errorf("password is empty"), nil)
}
rsp := CreateResponse{
Username: r.Username,
}
// Show the user information.
SendResponse(c, nil, rsp)
}
================================================
FILE: demo06/handler/user/user.go
================================================
package user
type CreateRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type CreateResponse struct {
Username string `json:"username"`
}
================================================
FILE: demo06/main.go
================================================
package main
import (
"errors"
"net/http"
"time"
"apiserver/config"
"apiserver/model"
"apiserver/router"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
// init config
if err := config.Init(*cfg); err != nil {
panic(err)
}
// init db
model.DB.Init()
defer model.DB.Close()
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
// Create the Gin engine.
g := gin.New()
middlewares := []gin.HandlerFunc{}
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middlewares...,
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Info("The router has been deployed successfully.")
}()
log.Infof("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Info(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < viper.GetInt("max_ping_count"); i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get(viper.GetString("url") + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Info("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo06/model/init.go
================================================
package model
import (
"fmt"
"github.com/lexkong/log"
"github.com/spf13/viper"
// MySQL driver.
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type Database struct {
Self *gorm.DB
Docker *gorm.DB
}
var DB *Database
func openDB(username, password, addr, name string) *gorm.DB {
config := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
username,
password,
addr,
name,
true,
//"Asia/Shanghai"),
"Local")
db, err := gorm.Open("mysql", config)
if err != nil {
log.Errorf(err, "Database connection failed. Database name: %s", name)
}
// set for db connection
setupDB(db)
return db
}
func setupDB(db *gorm.DB) {
db.LogMode(viper.GetBool("gormlog"))
//db.DB().SetMaxOpenConns(20000) // 用于设置最大打开的连接数,默认值为0表示不限制.设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。
db.DB().SetMaxIdleConns(0) // 用于设置闲置的连接数.设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。
}
// used for cli
func InitSelfDB() *gorm.DB {
return openDB(viper.GetString("db.username"),
viper.GetString("db.password"),
viper.GetString("db.addr"),
viper.GetString("db.name"))
}
func GetSelfDB() *gorm.DB {
return InitSelfDB()
}
func InitDockerDB() *gorm.DB {
return openDB(viper.GetString("docker_db.username"),
viper.GetString("docker_db.password"),
viper.GetString("docker_db.addr"),
viper.GetString("docker_db.name"))
}
func GetDockerDB() *gorm.DB {
return InitDockerDB()
}
func (db *Database) Init() {
DB = &Database{
Self: GetSelfDB(),
Docker: GetDockerDB(),
}
}
func (db *Database) Close() {
DB.Self.Close()
DB.Docker.Close()
}
================================================
FILE: demo06/pkg/errno/code.go
================================================
package errno
var (
// Common errors
OK = &Errno{Code: 0, Message: "OK"}
InternalServerError = &Errno{Code: 10001, Message: "Internal server error."}
ErrBind = &Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct."}
// user errors
ErrUserNotFound = &Errno{Code: 20102, Message: "The user was not found."}
)
================================================
FILE: demo06/pkg/errno/errno.go
================================================
package errno
import "fmt"
type Errno struct {
Code int
Message string
}
func (err Errno) Error() string {
return err.Message
}
// Err represents an error
type Err struct {
Code int
Message string
Err error
}
func New(errno *Errno, err error) *Err {
return &Err{Code: errno.Code, Message: errno.Message, Err: err}
}
func (err *Err) Add(message string) error {
//err.Message = fmt.Sprintf("%s %s", err.Message, message)
err.Message += " " + message
return err
}
func (err *Err) Addf(format string, args ...interface{}) error {
//return err.Message = fmt.Sprintf("%s %s", err.Message, fmt.Sprintf(format, args...))
err.Message += " " + fmt.Sprintf(format, args...)
return err
}
func (err *Err) Error() string {
return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err)
}
func IsErrUserNotFound(err error) bool {
code, _ := DecodeErr(err)
return code == ErrUserNotFound.Code
}
func DecodeErr(err error) (int, string) {
if err == nil {
return OK.Code, OK.Message
}
switch typed := err.(type) {
case *Err:
return typed.Code, typed.Message
case *Errno:
return typed.Code, typed.Message
default:
}
return InternalServerError.Code, err.Error()
}
================================================
FILE: demo06/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo06/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/handler/user"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
u := g.Group("/v1/user")
{
u.POST("/:username", user.Create)
}
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo07/README.md
================================================
实战:用户业务逻辑处理(业务处理)
================================================
FILE: demo07/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
log:
writers: file,stdout
logger_level: DEBUG
logger_file: log/apiserver.log
log_format_text: false
rollingPolicy: size
log_rotate_date: 1
log_rotate_size: 1
log_backup_count: 7
db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
docker_db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
================================================
FILE: demo07/config/config.go
================================================
package config
import (
"strings"
"github.com/fsnotify/fsnotify"
"github.com/lexkong/log"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 初始化日志包
c.initLog()
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
func (c *Config) initLog() {
passLagerCfg := log.PassLagerCfg{
Writers: viper.GetString("log.writers"),
LoggerLevel: viper.GetString("log.logger_level"),
LoggerFile: viper.GetString("log.logger_file"),
LogFormatText: viper.GetBool("log.log_format_text"),
RollingPolicy: viper.GetString("log.rollingPolicy"),
LogRotateDate: viper.GetInt("log.log_rotate_date"),
LogRotateSize: viper.GetInt("log.log_rotate_size"),
LogBackupCount: viper.GetInt("log.log_backup_count"),
}
log.InitWithConfig(&passLagerCfg)
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Infof("Config file changed: %s", e.Name)
})
}
================================================
FILE: demo07/db.sql
================================================
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `db_apiserver` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `db_apiserver`;
--
-- Table structure for table `tb_users`
--
DROP TABLE IF EXISTS `tb_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tb_users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`createdAt` timestamp NULL DEFAULT NULL,
`updatedAt` timestamp NULL DEFAULT NULL,
`deletedAt` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
KEY `idx_tb_users_deletedAt` (`deletedAt`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tb_users`
--
LOCK TABLES `tb_users` WRITE;
/*!40000 ALTER TABLE `tb_users` DISABLE KEYS */;
INSERT INTO `tb_users` VALUES (0,'admin','$2a$10$veGcArz47VGj7l9xN7g2iuT9TF21jLI1YGXarGzvARNdnt4inC9PG','2018-05-27 16:25:33','2018-05-27 16:25:33',NULL);
/*!40000 ALTER TABLE `tb_users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2018-05-28 0:25:41
================================================
FILE: demo07/handler/handler.go
================================================
package handler
import (
"net/http"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func SendResponse(c *gin.Context, err error, data interface{}) {
code, message := errno.DecodeErr(err)
// always return http.StatusOK
c.JSON(http.StatusOK, Response{
Code: code,
Message: message,
Data: data,
})
}
================================================
FILE: demo07/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo07/handler/user/create.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"apiserver/util"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/lexkong/log/lager"
)
// Create creates a new user account.
func Create(c *gin.Context) {
log.Info("User Create function called.", lager.Data{"X-Request-Id": util.GetReqID(c)})
var r CreateRequest
if err := c.Bind(&r); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
u := model.UserModel{
Username: r.Username,
Password: r.Password,
}
// Validate the data.
if err := u.Validate(); err != nil {
SendResponse(c, errno.ErrValidation, nil)
return
}
// Encrypt the user password.
if err := u.Encrypt(); err != nil {
SendResponse(c, errno.ErrEncrypt, nil)
return
}
// Insert the user to the database.
if err := u.Create(); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
rsp := CreateResponse{
Username: r.Username,
}
// Show the user information.
SendResponse(c, nil, rsp)
}
================================================
FILE: demo07/handler/user/delete.go
================================================
package user
import (
"strconv"
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
// Delete delete an user by the user identifier.
func Delete(c *gin.Context) {
userId, _ := strconv.Atoi(c.Param("id"))
if err := model.DeleteUser(uint64(userId)); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
SendResponse(c, nil, nil)
}
================================================
FILE: demo07/handler/user/get.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
// Get gets an user by the user identifier.
func Get(c *gin.Context) {
username := c.Param("username")
// Get the user by the `username` from the database.
user, err := model.GetUser(username)
if err != nil {
SendResponse(c, errno.ErrUserNotFound, nil)
return
}
SendResponse(c, nil, user)
}
================================================
FILE: demo07/handler/user/list.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/pkg/errno"
"apiserver/service"
"github.com/gin-gonic/gin"
)
// List list the users in the database.
func List(c *gin.Context) {
var r ListRequest
if err := c.Bind(&r); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
infos, count, err := service.ListUser(r.Username, r.Offset, r.Limit)
if err != nil {
SendResponse(c, err, nil)
return
}
SendResponse(c, nil, ListResponse{
TotalCount: count,
UserList: infos,
})
}
================================================
FILE: demo07/handler/user/update.go
================================================
package user
import (
"strconv"
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"apiserver/util"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/lexkong/log/lager"
)
// Update update a exist user account info.
func Update(c *gin.Context) {
log.Info("Update function called.", lager.Data{"X-Request-Id": util.GetReqID(c)})
// Get the user id from the url parameter.
userId, _ := strconv.Atoi(c.Param("id"))
// Binding the user data.
var u model.UserModel
if err := c.Bind(&u); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
// We update the record based on the user id.
u.Id = uint64(userId)
// Validate the data.
if err := u.Validate(); err != nil {
SendResponse(c, errno.ErrValidation, nil)
return
}
// Encrypt the user password.
if err := u.Encrypt(); err != nil {
SendResponse(c, errno.ErrEncrypt, nil)
return
}
// Save changed fields.
if err := u.Update(); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
SendResponse(c, nil, nil)
}
================================================
FILE: demo07/handler/user/user.go
================================================
package user
import (
"apiserver/model"
)
type CreateRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type CreateResponse struct {
Username string `json:"username"`
}
type ListRequest struct {
Username string `json:"username"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
type ListResponse struct {
TotalCount uint64 `json:"totalCount"`
UserList []*model.UserInfo `json:"userList"`
}
================================================
FILE: demo07/main.go
================================================
package main
import (
"errors"
"net/http"
"time"
"apiserver/config"
"apiserver/model"
"apiserver/router"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
// init config
if err := config.Init(*cfg); err != nil {
panic(err)
}
// init db
model.DB.Init()
defer model.DB.Close()
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
// Create the Gin engine.
g := gin.New()
middlewares := []gin.HandlerFunc{}
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middlewares...,
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Info("The router has been deployed successfully.")
}()
log.Infof("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Info(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < viper.GetInt("max_ping_count"); i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get(viper.GetString("url") + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Info("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo07/model/init.go
================================================
package model
import (
"fmt"
"github.com/lexkong/log"
"github.com/spf13/viper"
// MySQL driver.
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type Database struct {
Self *gorm.DB
Docker *gorm.DB
}
var DB *Database
func openDB(username, password, addr, name string) *gorm.DB {
config := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
username,
password,
addr,
name,
true,
//"Asia/Shanghai"),
"Local")
db, err := gorm.Open("mysql", config)
if err != nil {
log.Errorf(err, "Database connection failed. Database name: %s", name)
}
// set for db connection
setupDB(db)
return db
}
func setupDB(db *gorm.DB) {
db.LogMode(viper.GetBool("gormlog"))
//db.DB().SetMaxOpenConns(20000) // 用于设置最大打开的连接数,默认值为0表示不限制.设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。
db.DB().SetMaxIdleConns(0) // 用于设置闲置的连接数.设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。
}
// used for cli
func InitSelfDB() *gorm.DB {
return openDB(viper.GetString("db.username"),
viper.GetString("db.password"),
viper.GetString("db.addr"),
viper.GetString("db.name"))
}
func GetSelfDB() *gorm.DB {
return InitSelfDB()
}
func InitDockerDB() *gorm.DB {
return openDB(viper.GetString("docker_db.username"),
viper.GetString("docker_db.password"),
viper.GetString("docker_db.addr"),
viper.GetString("docker_db.name"))
}
func GetDockerDB() *gorm.DB {
return InitDockerDB()
}
func (db *Database) Init() {
DB = &Database{
Self: GetSelfDB(),
Docker: GetDockerDB(),
}
}
func (db *Database) Close() {
DB.Self.Close()
DB.Docker.Close()
}
================================================
FILE: demo07/model/model.go
================================================
package model
import (
"sync"
"time"
)
type BaseModel struct {
Id uint64 `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"-"`
CreatedAt time.Time `gorm:"column:createdAt" json:"-"`
UpdatedAt time.Time `gorm:"column:updatedAt" json:"-"`
DeletedAt *time.Time `gorm:"column:deletedAt" sql:"index" json:"-"`
}
type UserInfo struct {
Id uint64 `json:"id"`
Username string `json:"username"`
SayHello string `json:"sayHello"`
Password string `json:"password"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type UserList struct {
Lock *sync.Mutex
IdMap map[uint64]*UserInfo
}
// Token represents a JSON web token.
type Token struct {
Token string `json:"token"`
}
================================================
FILE: demo07/model/user.go
================================================
package model
import (
"fmt"
"apiserver/pkg/auth"
"apiserver/pkg/constvar"
validator "gopkg.in/go-playground/validator.v9"
)
// User represents a registered user.
type UserModel struct {
BaseModel
Username string `json:"username" gorm:"column:username;not null" binding:"required" validate:"min=1,max=32"`
Password string `json:"password" gorm:"column:password;not null" binding:"required" validate:"min=5,max=128"`
}
func (c *UserModel) TableName() string {
return "tb_users"
}
// Create creates a new user account.
func (u *UserModel) Create() error {
return DB.Self.Create(&u).Error
}
// DeleteUser deletes the user by the user identifier.
func DeleteUser(id uint64) error {
user := UserModel{}
user.BaseModel.Id = id
return DB.Self.Delete(&user).Error
}
// Update updates an user account information.
func (u *UserModel) Update() error {
return DB.Self.Save(u).Error
}
// GetUser gets an user by the user identifier.
func GetUser(username string) (*UserModel, error) {
u := &UserModel{}
d := DB.Self.Where("username = ?", username).First(&u)
return u, d.Error
}
// ListUser List all users
func ListUser(username string, offset, limit int) ([]*UserModel, uint64, error) {
if limit == 0 {
limit = constvar.DefaultLimit
}
users := make([]*UserModel, 0)
var count uint64
where := fmt.Sprintf("username like '%%%s%%'", username)
if err := DB.Self.Model(&UserModel{}).Where(where).Count(&count).Error; err != nil {
return users, count, err
}
if err := DB.Self.Where(where).Offset(offset).Limit(limit).Order("id desc").Find(&users).Error; err != nil {
return users, count, err
}
return users, count, nil
}
// Compare with the plain text password. Returns true if it's the same as the encrypted one (in the `User` struct).
func (u *UserModel) Compare(pwd string) (err error) {
err = auth.Compare(u.Password, pwd)
return
}
// Encrypt the user password.
func (u *UserModel) Encrypt() (err error) {
u.Password, err = auth.Encrypt(u.Password)
return
}
// Validate the fields.
func (u *UserModel) Validate() error {
validate := validator.New()
return validate.Struct(u)
}
================================================
FILE: demo07/pkg/auth/auth.go
================================================
package auth
import "golang.org/x/crypto/bcrypt"
// Encrypt encrypts the plain text with bcrypt.
func Encrypt(source string) (string, error) {
hashedBytes, err := bcrypt.GenerateFromPassword([]byte(source), bcrypt.DefaultCost)
return string(hashedBytes), err
}
// Compare compares the encrypted text with the plain text if it's the same.
func Compare(hashedPassword, password string) error {
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}
================================================
FILE: demo07/pkg/constvar/constvar.go
================================================
package constvar
const (
DefaultLimit = 50
)
================================================
FILE: demo07/pkg/errno/code.go
================================================
package errno
var (
// Common errors
OK = &Errno{Code: 0, Message: "OK"}
InternalServerError = &Errno{Code: 10001, Message: "Internal server error"}
ErrBind = &Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct."}
ErrValidation = &Errno{Code: 20001, Message: "Validation failed."}
ErrDatabase = &Errno{Code: 20002, Message: "Database error."}
ErrToken = &Errno{Code: 20003, Message: "Error occurred while signing the JSON web token."}
// user errors
ErrEncrypt = &Errno{Code: 20101, Message: "Error occurred while encrypting the user password."}
ErrUserNotFound = &Errno{Code: 20102, Message: "The user was not found."}
ErrTokenInvalid = &Errno{Code: 20103, Message: "The token was invalid."}
ErrPasswordIncorrect = &Errno{Code: 20104, Message: "The password was incorrect."}
)
================================================
FILE: demo07/pkg/errno/errno.go
================================================
package errno
import "fmt"
type Errno struct {
Code int
Message string
}
func (err Errno) Error() string {
return err.Message
}
// Err represents an error
type Err struct {
Code int
Message string
Err error
}
func New(errno *Errno, err error) *Err {
return &Err{Code: errno.Code, Message: errno.Message, Err: err}
}
func (err *Err) Add(message string) error {
err.Message += " " + message
return err
}
func (err *Err) Addf(format string, args ...interface{}) error {
err.Message += " " + fmt.Sprintf(format, args...)
return err
}
func (err *Err) Error() string {
return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err)
}
func IsErrUserNotFound(err error) bool {
code, _ := DecodeErr(err)
return code == ErrUserNotFound.Code
}
func DecodeErr(err error) (int, string) {
if err == nil {
return OK.Code, OK.Message
}
switch typed := err.(type) {
case *Err:
return typed.Code, typed.Message
case *Errno:
return typed.Code, typed.Message
default:
}
return InternalServerError.Code, err.Error()
}
================================================
FILE: demo07/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo07/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/handler/user"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
u := g.Group("/v1/user")
{
u.POST("", user.Create)
u.DELETE("/:id", user.Delete)
u.PUT("/:id", user.Update)
u.GET("", user.List)
u.GET("/:username", user.Get)
}
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo07/service/service.go
================================================
package service
import (
"fmt"
"sync"
"apiserver/model"
"apiserver/util"
)
func ListUser(username string, offset, limit int) ([]*model.UserInfo, uint64, error) {
infos := make([]*model.UserInfo, 0)
users, count, err := model.ListUser(username, offset, limit)
if err != nil {
return nil, count, err
}
ids := []uint64{}
for _, user := range users {
ids = append(ids, user.Id)
}
wg := sync.WaitGroup{}
userList := model.UserList{
Lock: new(sync.Mutex),
IdMap: make(map[uint64]*model.UserInfo, len(users)),
}
errChan := make(chan error, 1)
finished := make(chan bool, 1)
// Improve query efficiency in parallel
for _, u := range users {
wg.Add(1)
go func(u *model.UserModel) {
defer wg.Done()
shortId, err := util.GenShortId()
if err != nil {
errChan <- err
return
}
userList.Lock.Lock()
defer userList.Lock.Unlock()
userList.IdMap[u.Id] = &model.UserInfo{
Id: u.Id,
Username: u.Username,
SayHello: fmt.Sprintf("Hello %s", shortId),
Password: u.Password,
CreatedAt: u.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: u.UpdatedAt.Format("2006-01-02 15:04:05"),
}
}(u)
}
go func() {
wg.Wait()
close(finished)
}()
select {
case <-finished:
case err := <-errChan:
return nil, count, err
}
for _, id := range ids {
infos = append(infos, userList.IdMap[id])
}
return infos, count, nil
}
================================================
FILE: demo07/util/util.go
================================================
package util
import (
"github.com/gin-gonic/gin"
"github.com/teris-io/shortid"
)
func GenShortId() (string, error) {
return shortid.Generate()
}
func GetReqID(c *gin.Context) string {
v, ok := c.Get("X-Request-Id")
if !ok {
return ""
}
if requestId, ok := v.(string); ok {
return requestId
}
return ""
}
================================================
FILE: demo08/README.md
================================================
实战:HTTP调用添加自定义处理逻辑
================================================
FILE: demo08/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
log:
writers: file,stdout
logger_level: DEBUG
logger_file: log/apiserver.log
log_format_text: false
rollingPolicy: size
log_rotate_date: 1
log_rotate_size: 1
log_backup_count: 7
db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
docker_db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
================================================
FILE: demo08/config/config.go
================================================
package config
import (
"strings"
"github.com/fsnotify/fsnotify"
"github.com/lexkong/log"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 初始化日志包
c.initLog()
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
func (c *Config) initLog() {
passLagerCfg := log.PassLagerCfg{
Writers: viper.GetString("log.writers"),
LoggerLevel: viper.GetString("log.logger_level"),
LoggerFile: viper.GetString("log.logger_file"),
LogFormatText: viper.GetBool("log.log_format_text"),
RollingPolicy: viper.GetString("log.rollingPolicy"),
LogRotateDate: viper.GetInt("log.log_rotate_date"),
LogRotateSize: viper.GetInt("log.log_rotate_size"),
LogBackupCount: viper.GetInt("log.log_backup_count"),
}
log.InitWithConfig(&passLagerCfg)
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Infof("Config file changed: %s", e.Name)
})
}
================================================
FILE: demo08/db.sql
================================================
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `db_apiserver` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `db_apiserver`;
--
-- Table structure for table `tb_users`
--
DROP TABLE IF EXISTS `tb_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tb_users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`createdAt` timestamp NULL DEFAULT NULL,
`updatedAt` timestamp NULL DEFAULT NULL,
`deletedAt` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
KEY `idx_tb_users_deletedAt` (`deletedAt`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tb_users`
--
LOCK TABLES `tb_users` WRITE;
/*!40000 ALTER TABLE `tb_users` DISABLE KEYS */;
INSERT INTO `tb_users` VALUES (0,'admin','$2a$10$veGcArz47VGj7l9xN7g2iuT9TF21jLI1YGXarGzvARNdnt4inC9PG','2018-05-27 16:25:33','2018-05-27 16:25:33',NULL);
/*!40000 ALTER TABLE `tb_users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2018-05-28 0:25:41
================================================
FILE: demo08/handler/handler.go
================================================
package handler
import (
"net/http"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func SendResponse(c *gin.Context, err error, data interface{}) {
code, message := errno.DecodeErr(err)
// always return http.StatusOK
c.JSON(http.StatusOK, Response{
Code: code,
Message: message,
Data: data,
})
}
================================================
FILE: demo08/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo08/handler/user/create.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"apiserver/util"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/lexkong/log/lager"
)
// Create creates a new user account.
func Create(c *gin.Context) {
log.Info("User Create function called.", lager.Data{"X-Request-Id": util.GetReqID(c)})
var r CreateRequest
if err := c.Bind(&r); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
u := model.UserModel{
Username: r.Username,
Password: r.Password,
}
// Validate the data.
if err := u.Validate(); err != nil {
SendResponse(c, errno.ErrValidation, nil)
return
}
// Encrypt the user password.
if err := u.Encrypt(); err != nil {
SendResponse(c, errno.ErrEncrypt, nil)
return
}
// Insert the user to the database.
if err := u.Create(); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
rsp := CreateResponse{
Username: r.Username,
}
// Show the user information.
SendResponse(c, nil, rsp)
}
================================================
FILE: demo08/handler/user/delete.go
================================================
package user
import (
"strconv"
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
// Delete delete an user by the user identifier.
func Delete(c *gin.Context) {
userId, _ := strconv.Atoi(c.Param("id"))
if err := model.DeleteUser(uint64(userId)); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
SendResponse(c, nil, nil)
}
================================================
FILE: demo08/handler/user/get.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
// Get gets an user by the user identifier.
func Get(c *gin.Context) {
username := c.Param("username")
// Get the user by the `username` from the database.
user, err := model.GetUser(username)
if err != nil {
SendResponse(c, errno.ErrUserNotFound, nil)
return
}
SendResponse(c, nil, user)
}
================================================
FILE: demo08/handler/user/list.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/pkg/errno"
"apiserver/service"
"github.com/gin-gonic/gin"
)
// List list the users in the database.
func List(c *gin.Context) {
var r ListRequest
if err := c.Bind(&r); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
infos, count, err := service.ListUser(r.Username, r.Offset, r.Limit)
if err != nil {
SendResponse(c, err, nil)
return
}
SendResponse(c, nil, ListResponse{
TotalCount: count,
UserList: infos,
})
}
================================================
FILE: demo08/handler/user/update.go
================================================
package user
import (
"strconv"
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"apiserver/util"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/lexkong/log/lager"
)
// Update update a exist user account info.
func Update(c *gin.Context) {
log.Info("Update function called.", lager.Data{"X-Request-Id": util.GetReqID(c)})
// Get the user id from the url parameter.
userId, _ := strconv.Atoi(c.Param("id"))
// Binding the user data.
var u model.UserModel
if err := c.Bind(&u); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
// We update the record based on the user id.
u.Id = uint64(userId)
// Validate the data.
if err := u.Validate(); err != nil {
SendResponse(c, errno.ErrValidation, nil)
return
}
// Encrypt the user password.
if err := u.Encrypt(); err != nil {
SendResponse(c, errno.ErrEncrypt, nil)
return
}
// Save changed fields.
if err := u.Update(); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
SendResponse(c, nil, nil)
}
================================================
FILE: demo08/handler/user/user.go
================================================
package user
import (
"apiserver/model"
)
type CreateRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type CreateResponse struct {
Username string `json:"username"`
}
type ListRequest struct {
Username string `json:"username"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
type ListResponse struct {
TotalCount uint64 `json:"totalCount"`
UserList []*model.UserInfo `json:"userList"`
}
================================================
FILE: demo08/main.go
================================================
package main
import (
"errors"
"net/http"
"time"
"apiserver/config"
"apiserver/model"
"apiserver/router"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
// init config
if err := config.Init(*cfg); err != nil {
panic(err)
}
// init db
model.DB.Init()
defer model.DB.Close()
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
// Create the Gin engine.
g := gin.New()
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middleware.Logging(),
middleware.RequestId(),
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Info("The router has been deployed successfully.")
}()
log.Infof("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Info(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < viper.GetInt("max_ping_count"); i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get(viper.GetString("url") + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Info("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo08/model/init.go
================================================
package model
import (
"fmt"
"github.com/lexkong/log"
"github.com/spf13/viper"
// MySQL driver.
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type Database struct {
Self *gorm.DB
Docker *gorm.DB
}
var DB *Database
func openDB(username, password, addr, name string) *gorm.DB {
config := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
username,
password,
addr,
name,
true,
//"Asia/Shanghai"),
"Local")
db, err := gorm.Open("mysql", config)
if err != nil {
log.Errorf(err, "Database connection failed. Database name: %s", name)
}
// set for db connection
setupDB(db)
return db
}
func setupDB(db *gorm.DB) {
db.LogMode(viper.GetBool("gormlog"))
//db.DB().SetMaxOpenConns(20000) // 用于设置最大打开的连接数,默认值为0表示不限制.设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。
db.DB().SetMaxIdleConns(0) // 用于设置闲置的连接数.设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。
}
// used for cli
func InitSelfDB() *gorm.DB {
return openDB(viper.GetString("db.username"),
viper.GetString("db.password"),
viper.GetString("db.addr"),
viper.GetString("db.name"))
}
func GetSelfDB() *gorm.DB {
return InitSelfDB()
}
func InitDockerDB() *gorm.DB {
return openDB(viper.GetString("docker_db.username"),
viper.GetString("docker_db.password"),
viper.GetString("docker_db.addr"),
viper.GetString("docker_db.name"))
}
func GetDockerDB() *gorm.DB {
return InitDockerDB()
}
func (db *Database) Init() {
DB = &Database{
Self: GetSelfDB(),
Docker: GetDockerDB(),
}
}
func (db *Database) Close() {
DB.Self.Close()
DB.Docker.Close()
}
================================================
FILE: demo08/model/model.go
================================================
package model
import (
"sync"
"time"
)
type BaseModel struct {
Id uint64 `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"-"`
CreatedAt time.Time `gorm:"column:createdAt" json:"-"`
UpdatedAt time.Time `gorm:"column:updatedAt" json:"-"`
DeletedAt *time.Time `gorm:"column:deletedAt" sql:"index" json:"-"`
}
type UserInfo struct {
Id uint64 `json:"id"`
Username string `json:"username"`
SayHello string `json:"sayHello"`
Password string `json:"password"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type UserList struct {
Lock *sync.Mutex
IdMap map[uint64]*UserInfo
}
// Token represents a JSON web token.
type Token struct {
Token string `json:"token"`
}
================================================
FILE: demo08/model/user.go
================================================
package model
import (
"fmt"
"apiserver/pkg/auth"
"apiserver/pkg/constvar"
validator "gopkg.in/go-playground/validator.v9"
)
// User represents a registered user.
type UserModel struct {
BaseModel
Username string `json:"username" gorm:"column:username;not null" binding:"required" validate:"min=1,max=32"`
Password string `json:"password" gorm:"column:password;not null" binding:"required" validate:"min=5,max=128"`
}
func (c *UserModel) TableName() string {
return "tb_users"
}
// Create creates a new user account.
func (u *UserModel) Create() error {
return DB.Self.Create(&u).Error
}
// DeleteUser deletes the user by the user identifier.
func DeleteUser(id uint64) error {
user := UserModel{}
user.BaseModel.Id = id
return DB.Self.Delete(&user).Error
}
// Update updates an user account information.
func (u *UserModel) Update() error {
return DB.Self.Save(u).Error
}
// GetUser gets an user by the user identifier.
func GetUser(username string) (*UserModel, error) {
u := &UserModel{}
d := DB.Self.Where("username = ?", username).First(&u)
return u, d.Error
}
// ListUser List all users
func ListUser(username string, offset, limit int) ([]*UserModel, uint64, error) {
if limit == 0 {
limit = constvar.DefaultLimit
}
users := make([]*UserModel, 0)
var count uint64
where := fmt.Sprintf("username like '%%%s%%'", username)
if err := DB.Self.Model(&UserModel{}).Where(where).Count(&count).Error; err != nil {
return users, count, err
}
if err := DB.Self.Where(where).Offset(offset).Limit(limit).Order("id desc").Find(&users).Error; err != nil {
return users, count, err
}
return users, count, nil
}
// Compare with the plain text password. Returns true if it's the same as the encrypted one (in the `User` struct).
func (u *UserModel) Compare(pwd string) (err error) {
err = auth.Compare(u.Password, pwd)
return
}
// Encrypt the user password.
func (u *UserModel) Encrypt() (err error) {
u.Password, err = auth.Encrypt(u.Password)
return
}
// Validate the fields.
func (u *UserModel) Validate() error {
validate := validator.New()
return validate.Struct(u)
}
================================================
FILE: demo08/pkg/auth/auth.go
================================================
package auth
import "golang.org/x/crypto/bcrypt"
// Encrypt encrypts the plain text with bcrypt.
func Encrypt(source string) (string, error) {
hashedBytes, err := bcrypt.GenerateFromPassword([]byte(source), bcrypt.DefaultCost)
return string(hashedBytes), err
}
// Compare compares the encrypted text with the plain text if it's the same.
func Compare(hashedPassword, password string) error {
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}
================================================
FILE: demo08/pkg/constvar/constvar.go
================================================
package constvar
const (
DefaultLimit = 50
)
================================================
FILE: demo08/pkg/errno/code.go
================================================
package errno
var (
// Common errors
OK = &Errno{Code: 0, Message: "OK"}
InternalServerError = &Errno{Code: 10001, Message: "Internal server error"}
ErrBind = &Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct."}
ErrValidation = &Errno{Code: 20001, Message: "Validation failed."}
ErrDatabase = &Errno{Code: 20002, Message: "Database error."}
ErrToken = &Errno{Code: 20003, Message: "Error occurred while signing the JSON web token."}
// user errors
ErrEncrypt = &Errno{Code: 20101, Message: "Error occurred while encrypting the user password."}
ErrUserNotFound = &Errno{Code: 20102, Message: "The user was not found."}
ErrTokenInvalid = &Errno{Code: 20103, Message: "The token was invalid."}
ErrPasswordIncorrect = &Errno{Code: 20104, Message: "The password was incorrect."}
)
================================================
FILE: demo08/pkg/errno/errno.go
================================================
package errno
import "fmt"
type Errno struct {
Code int
Message string
}
func (err Errno) Error() string {
return err.Message
}
// Err represents an error
type Err struct {
Code int
Message string
Err error
}
func New(errno *Errno, err error) *Err {
return &Err{Code: errno.Code, Message: errno.Message, Err: err}
}
func (err *Err) Add(message string) error {
err.Message += " " + message
return err
}
func (err *Err) Addf(format string, args ...interface{}) error {
err.Message += " " + fmt.Sprintf(format, args...)
return err
}
func (err *Err) Error() string {
return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err)
}
func IsErrUserNotFound(err error) bool {
code, _ := DecodeErr(err)
return code == ErrUserNotFound.Code
}
func DecodeErr(err error) (int, string) {
if err == nil {
return OK.Code, OK.Message
}
switch typed := err.(type) {
case *Err:
return typed.Code, typed.Message
case *Errno:
return typed.Code, typed.Message
default:
}
return InternalServerError.Code, err.Error()
}
================================================
FILE: demo08/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo08/router/middleware/logging.go
================================================
package middleware
import (
"bytes"
"encoding/json"
"io/ioutil"
"regexp"
"time"
"apiserver/handler"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/willf/pad"
)
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
// Logging is a middleware function that logs the each request.
func Logging() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now().UTC()
path := c.Request.URL.Path
reg := regexp.MustCompile("(/v1/user|/login)")
if !reg.MatchString(path) {
return
}
// Skip for the health check requests.
if path == "/sd/health" || path == "/sd/ram" || path == "/sd/cpu" || path == "/sd/disk" {
return
}
// Read the Body content
var bodyBytes []byte
if c.Request.Body != nil {
bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
}
// Restore the io.ReadCloser to its original state
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
// The basic informations.
method := c.Request.Method
ip := c.ClientIP()
//log.Debugf("New request come in, path: %s, Method: %s, body `%s`", path, method, string(bodyBytes))
blw := &bodyLogWriter{
body: bytes.NewBufferString(""),
ResponseWriter: c.Writer,
}
c.Writer = blw
// Continue.
c.Next()
// Calculates the latency.
end := time.Now().UTC()
latency := end.Sub(start)
code, message := -1, ""
// get code and message
var response handler.Response
if err := json.Unmarshal(blw.body.Bytes(), &response); err != nil {
log.Errorf(err, "response body can not unmarshal to model.Response struct, body: `%s`", blw.body.Bytes())
code = errno.InternalServerError.Code
message = err.Error()
} else {
code = response.Code
message = response.Message
}
log.Infof("%-13s | %-12s | %s %s | {code: %d, message: %s}", latency, ip, pad.Right(method, 5, ""), path, code, message)
}
}
================================================
FILE: demo08/router/middleware/requestid.go
================================================
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/satori/go.uuid"
)
func RequestId() gin.HandlerFunc {
return func(c *gin.Context) {
// Check for incoming header, use it if exists
requestId := c.Request.Header.Get("X-Request-Id")
// Create request id with UUID4
if requestId == "" {
u4, _ := uuid.NewV4()
requestId = u4.String()
}
// Expose it for use in the application
c.Set("X-Request-Id", requestId)
// Set X-Request-Id header
c.Writer.Header().Set("X-Request-Id", requestId)
c.Next()
}
}
================================================
FILE: demo08/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/handler/user"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
u := g.Group("/v1/user")
{
u.POST("", user.Create)
u.DELETE("/:id", user.Delete)
u.PUT("/:id", user.Update)
u.GET("", user.List)
u.GET("/:username", user.Get)
}
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo08/service/service.go
================================================
package service
import (
"fmt"
"sync"
"apiserver/model"
"apiserver/util"
)
func ListUser(username string, offset, limit int) ([]*model.UserInfo, uint64, error) {
infos := make([]*model.UserInfo, 0)
users, count, err := model.ListUser(username, offset, limit)
if err != nil {
return nil, count, err
}
ids := []uint64{}
for _, user := range users {
ids = append(ids, user.Id)
}
wg := sync.WaitGroup{}
userList := model.UserList{
Lock: new(sync.Mutex),
IdMap: make(map[uint64]*model.UserInfo, len(users)),
}
errChan := make(chan error, 1)
finished := make(chan bool, 1)
// Improve query efficiency in parallel
for _, u := range users {
wg.Add(1)
go func(u *model.UserModel) {
defer wg.Done()
shortId, err := util.GenShortId()
if err != nil {
errChan <- err
return
}
userList.Lock.Lock()
defer userList.Lock.Unlock()
userList.IdMap[u.Id] = &model.UserInfo{
Id: u.Id,
Username: u.Username,
SayHello: fmt.Sprintf("Hello %s", shortId),
Password: u.Password,
CreatedAt: u.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: u.UpdatedAt.Format("2006-01-02 15:04:05"),
}
}(u)
}
go func() {
wg.Wait()
close(finished)
}()
select {
case <-finished:
case err := <-errChan:
return nil, count, err
}
for _, id := range ids {
infos = append(infos, userList.IdMap[id])
}
return infos, count, nil
}
================================================
FILE: demo08/util/util.go
================================================
package util
import (
"github.com/gin-gonic/gin"
"github.com/teris-io/shortid"
)
func GenShortId() (string, error) {
return shortid.Generate()
}
func GetReqID(c *gin.Context) string {
v, ok := c.Get("X-Request-Id")
if !ok {
return ""
}
if requestId, ok := v.(string); ok {
return requestId
}
return ""
}
================================================
FILE: demo09/README.md
================================================
实战:API身份验证
================================================
FILE: demo09/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
jwt_secret: Rtg8BPKNEf2mB4mgvKONGPZZQSaJWNLijxR42qRgq0iBb5
log:
writers: file,stdout
logger_level: DEBUG
logger_file: log/apiserver.log
log_format_text: false
rollingPolicy: size
log_rotate_date: 1
log_rotate_size: 1
log_backup_count: 7
db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
docker_db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
================================================
FILE: demo09/config/config.go
================================================
package config
import (
"strings"
"github.com/fsnotify/fsnotify"
"github.com/lexkong/log"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 初始化日志包
c.initLog()
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
func (c *Config) initLog() {
passLagerCfg := log.PassLagerCfg{
Writers: viper.GetString("log.writers"),
LoggerLevel: viper.GetString("log.logger_level"),
LoggerFile: viper.GetString("log.logger_file"),
LogFormatText: viper.GetBool("log.log_format_text"),
RollingPolicy: viper.GetString("log.rollingPolicy"),
LogRotateDate: viper.GetInt("log.log_rotate_date"),
LogRotateSize: viper.GetInt("log.log_rotate_size"),
LogBackupCount: viper.GetInt("log.log_backup_count"),
}
log.InitWithConfig(&passLagerCfg)
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Infof("Config file changed: %s", e.Name)
})
}
================================================
FILE: demo09/db.sql
================================================
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `db_apiserver` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `db_apiserver`;
--
-- Table structure for table `tb_users`
--
DROP TABLE IF EXISTS `tb_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tb_users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`createdAt` timestamp NULL DEFAULT NULL,
`updatedAt` timestamp NULL DEFAULT NULL,
`deletedAt` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
KEY `idx_tb_users_deletedAt` (`deletedAt`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tb_users`
--
LOCK TABLES `tb_users` WRITE;
/*!40000 ALTER TABLE `tb_users` DISABLE KEYS */;
INSERT INTO `tb_users` VALUES (0,'admin','$2a$10$veGcArz47VGj7l9xN7g2iuT9TF21jLI1YGXarGzvARNdnt4inC9PG','2018-05-27 16:25:33','2018-05-27 16:25:33',NULL);
/*!40000 ALTER TABLE `tb_users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2018-05-28 0:25:41
================================================
FILE: demo09/handler/handler.go
================================================
package handler
import (
"net/http"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func SendResponse(c *gin.Context, err error, data interface{}) {
code, message := errno.DecodeErr(err)
// always return http.StatusOK
c.JSON(http.StatusOK, Response{
Code: code,
Message: message,
Data: data,
})
}
================================================
FILE: demo09/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo09/handler/user/create.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"apiserver/util"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/lexkong/log/lager"
)
// Create creates a new user account.
func Create(c *gin.Context) {
log.Info("User Create function called.", lager.Data{"X-Request-Id": util.GetReqID(c)})
var r CreateRequest
if err := c.Bind(&r); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
u := model.UserModel{
Username: r.Username,
Password: r.Password,
}
// Validate the data.
if err := u.Validate(); err != nil {
SendResponse(c, errno.ErrValidation, nil)
return
}
// Encrypt the user password.
if err := u.Encrypt(); err != nil {
SendResponse(c, errno.ErrEncrypt, nil)
return
}
// Insert the user to the database.
if err := u.Create(); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
rsp := CreateResponse{
Username: r.Username,
}
// Show the user information.
SendResponse(c, nil, rsp)
}
================================================
FILE: demo09/handler/user/delete.go
================================================
package user
import (
"strconv"
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
// Delete delete an user by the user identifier.
func Delete(c *gin.Context) {
userId, _ := strconv.Atoi(c.Param("id"))
if err := model.DeleteUser(uint64(userId)); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
SendResponse(c, nil, nil)
}
================================================
FILE: demo09/handler/user/get.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
// Get gets an user by the user identifier.
func Get(c *gin.Context) {
username := c.Param("username")
// Get the user by the `username` from the database.
user, err := model.GetUser(username)
if err != nil {
SendResponse(c, errno.ErrUserNotFound, nil)
return
}
SendResponse(c, nil, user)
}
================================================
FILE: demo09/handler/user/list.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/pkg/errno"
"apiserver/service"
"github.com/gin-gonic/gin"
)
// List list the users in the database.
func List(c *gin.Context) {
var r ListRequest
if err := c.Bind(&r); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
infos, count, err := service.ListUser(r.Username, r.Offset, r.Limit)
if err != nil {
SendResponse(c, err, nil)
return
}
SendResponse(c, nil, ListResponse{
TotalCount: count,
UserList: infos,
})
}
================================================
FILE: demo09/handler/user/login.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/auth"
"apiserver/pkg/errno"
"apiserver/pkg/token"
"github.com/gin-gonic/gin"
)
// Login generates the authentication token
// if the password was matched with the specified account.
func Login(c *gin.Context) {
// Binding the data with the user struct.
var u model.UserModel
if err := c.Bind(&u); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
// Get the user information by the login username.
d, err := model.GetUser(u.Username)
if err != nil {
SendResponse(c, errno.ErrUserNotFound, nil)
return
}
// Compare the login password with the user password.
if err := auth.Compare(d.Password, u.Password); err != nil {
SendResponse(c, errno.ErrPasswordIncorrect, nil)
return
}
// Sign the json web token.
t, err := token.Sign(c, token.Context{ID: d.Id, Username: d.Username}, "")
if err != nil {
SendResponse(c, errno.ErrToken, nil)
return
}
SendResponse(c, nil, model.Token{Token: t})
}
================================================
FILE: demo09/handler/user/update.go
================================================
package user
import (
"strconv"
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"apiserver/util"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/lexkong/log/lager"
)
// Update update a exist user account info.
func Update(c *gin.Context) {
log.Info("Update function called.", lager.Data{"X-Request-Id": util.GetReqID(c)})
// Get the user id from the url parameter.
userId, _ := strconv.Atoi(c.Param("id"))
// Binding the user data.
var u model.UserModel
if err := c.Bind(&u); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
// We update the record based on the user id.
u.Id = uint64(userId)
// Validate the data.
if err := u.Validate(); err != nil {
SendResponse(c, errno.ErrValidation, nil)
return
}
// Encrypt the user password.
if err := u.Encrypt(); err != nil {
SendResponse(c, errno.ErrEncrypt, nil)
return
}
// Save changed fields.
if err := u.Update(); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
SendResponse(c, nil, nil)
}
================================================
FILE: demo09/handler/user/user.go
================================================
package user
import (
"apiserver/model"
)
type CreateRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type CreateResponse struct {
Username string `json:"username"`
}
type ListRequest struct {
Username string `json:"username"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
type ListResponse struct {
TotalCount uint64 `json:"totalCount"`
UserList []*model.UserInfo `json:"userList"`
}
================================================
FILE: demo09/main.go
================================================
package main
import (
"errors"
"net/http"
"time"
"apiserver/config"
"apiserver/model"
"apiserver/router"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
// init config
if err := config.Init(*cfg); err != nil {
panic(err)
}
// init db
model.DB.Init()
defer model.DB.Close()
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
// Create the Gin engine.
g := gin.New()
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middleware.Logging(),
middleware.RequestId(),
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Info("The router has been deployed successfully.")
}()
log.Infof("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Info(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < viper.GetInt("max_ping_count"); i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get(viper.GetString("url") + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Info("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo09/model/init.go
================================================
package model
import (
"fmt"
"github.com/lexkong/log"
"github.com/spf13/viper"
// MySQL driver.
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type Database struct {
Self *gorm.DB
Docker *gorm.DB
}
var DB *Database
func openDB(username, password, addr, name string) *gorm.DB {
config := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
username,
password,
addr,
name,
true,
//"Asia/Shanghai"),
"Local")
db, err := gorm.Open("mysql", config)
if err != nil {
log.Errorf(err, "Database connection failed. Database name: %s", name)
}
// set for db connection
setupDB(db)
return db
}
func setupDB(db *gorm.DB) {
db.LogMode(viper.GetBool("gormlog"))
//db.DB().SetMaxOpenConns(20000) // 用于设置最大打开的连接数,默认值为0表示不限制.设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。
db.DB().SetMaxIdleConns(0) // 用于设置闲置的连接数.设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。
}
// used for cli
func InitSelfDB() *gorm.DB {
return openDB(viper.GetString("db.username"),
viper.GetString("db.password"),
viper.GetString("db.addr"),
viper.GetString("db.name"))
}
func GetSelfDB() *gorm.DB {
return InitSelfDB()
}
func InitDockerDB() *gorm.DB {
return openDB(viper.GetString("docker_db.username"),
viper.GetString("docker_db.password"),
viper.GetString("docker_db.addr"),
viper.GetString("docker_db.name"))
}
func GetDockerDB() *gorm.DB {
return InitDockerDB()
}
func (db *Database) Init() {
DB = &Database{
Self: GetSelfDB(),
Docker: GetDockerDB(),
}
}
func (db *Database) Close() {
DB.Self.Close()
DB.Docker.Close()
}
================================================
FILE: demo09/model/model.go
================================================
package model
import (
"sync"
"time"
)
type BaseModel struct {
Id uint64 `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"-"`
CreatedAt time.Time `gorm:"column:createdAt" json:"-"`
UpdatedAt time.Time `gorm:"column:updatedAt" json:"-"`
DeletedAt *time.Time `gorm:"column:deletedAt" sql:"index" json:"-"`
}
type UserInfo struct {
Id uint64 `json:"id"`
Username string `json:"username"`
SayHello string `json:"sayHello"`
Password string `json:"password"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type UserList struct {
Lock *sync.Mutex
IdMap map[uint64]*UserInfo
}
// Token represents a JSON web token.
type Token struct {
Token string `json:"token"`
}
================================================
FILE: demo09/model/user.go
================================================
package model
import (
"fmt"
"apiserver/pkg/auth"
"apiserver/pkg/constvar"
validator "gopkg.in/go-playground/validator.v9"
)
// User represents a registered user.
type UserModel struct {
BaseModel
Username string `json:"username" gorm:"column:username;not null" binding:"required" validate:"min=1,max=32"`
Password string `json:"password" gorm:"column:password;not null" binding:"required" validate:"min=5,max=128"`
}
func (c *UserModel) TableName() string {
return "tb_users"
}
// Create creates a new user account.
func (u *UserModel) Create() error {
return DB.Self.Create(&u).Error
}
// DeleteUser deletes the user by the user identifier.
func DeleteUser(id uint64) error {
user := UserModel{}
user.BaseModel.Id = id
return DB.Self.Delete(&user).Error
}
// Update updates an user account information.
func (u *UserModel) Update() error {
return DB.Self.Save(u).Error
}
// GetUser gets an user by the user identifier.
func GetUser(username string) (*UserModel, error) {
u := &UserModel{}
d := DB.Self.Where("username = ?", username).First(&u)
return u, d.Error
}
// ListUser List all users
func ListUser(username string, offset, limit int) ([]*UserModel, uint64, error) {
if limit == 0 {
limit = constvar.DefaultLimit
}
users := make([]*UserModel, 0)
var count uint64
where := fmt.Sprintf("username like '%%%s%%'", username)
if err := DB.Self.Model(&UserModel{}).Where(where).Count(&count).Error; err != nil {
return users, count, err
}
if err := DB.Self.Where(where).Offset(offset).Limit(limit).Order("id desc").Find(&users).Error; err != nil {
return users, count, err
}
return users, count, nil
}
// Compare with the plain text password. Returns true if it's the same as the encrypted one (in the `User` struct).
func (u *UserModel) Compare(pwd string) (err error) {
err = auth.Compare(u.Password, pwd)
return
}
// Encrypt the user password.
func (u *UserModel) Encrypt() (err error) {
u.Password, err = auth.Encrypt(u.Password)
return
}
// Validate the fields.
func (u *UserModel) Validate() error {
validate := validator.New()
return validate.Struct(u)
}
================================================
FILE: demo09/pkg/auth/auth.go
================================================
package auth
import "golang.org/x/crypto/bcrypt"
// Encrypt encrypts the plain text with bcrypt.
func Encrypt(source string) (string, error) {
hashedBytes, err := bcrypt.GenerateFromPassword([]byte(source), bcrypt.DefaultCost)
return string(hashedBytes), err
}
// Compare compares the encrypted text with the plain text if it's the same.
func Compare(hashedPassword, password string) error {
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}
================================================
FILE: demo09/pkg/constvar/constvar.go
================================================
package constvar
const (
DefaultLimit = 50
)
================================================
FILE: demo09/pkg/errno/code.go
================================================
package errno
var (
// Common errors
OK = &Errno{Code: 0, Message: "OK"}
InternalServerError = &Errno{Code: 10001, Message: "Internal server error"}
ErrBind = &Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct."}
ErrValidation = &Errno{Code: 20001, Message: "Validation failed."}
ErrDatabase = &Errno{Code: 20002, Message: "Database error."}
ErrToken = &Errno{Code: 20003, Message: "Error occurred while signing the JSON web token."}
// user errors
ErrEncrypt = &Errno{Code: 20101, Message: "Error occurred while encrypting the user password."}
ErrUserNotFound = &Errno{Code: 20102, Message: "The user was not found."}
ErrTokenInvalid = &Errno{Code: 20103, Message: "The token was invalid."}
ErrPasswordIncorrect = &Errno{Code: 20104, Message: "The password was incorrect."}
)
================================================
FILE: demo09/pkg/errno/errno.go
================================================
package errno
import "fmt"
type Errno struct {
Code int
Message string
}
func (err Errno) Error() string {
return err.Message
}
// Err represents an error
type Err struct {
Code int
Message string
Err error
}
func New(errno *Errno, err error) *Err {
return &Err{Code: errno.Code, Message: errno.Message, Err: err}
}
func (err *Err) Add(message string) error {
err.Message += " " + message
return err
}
func (err *Err) Addf(format string, args ...interface{}) error {
err.Message += " " + fmt.Sprintf(format, args...)
return err
}
func (err *Err) Error() string {
return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err)
}
func IsErrUserNotFound(err error) bool {
code, _ := DecodeErr(err)
return code == ErrUserNotFound.Code
}
func DecodeErr(err error) (int, string) {
if err == nil {
return OK.Code, OK.Message
}
switch typed := err.(type) {
case *Err:
return typed.Code, typed.Message
case *Errno:
return typed.Code, typed.Message
default:
}
return InternalServerError.Code, err.Error()
}
================================================
FILE: demo09/pkg/token/token.go
================================================
package token
import (
"errors"
"fmt"
"time"
jwt "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
var (
// ErrMissingHeader means the `Authorization` header was empty.
ErrMissingHeader = errors.New("The length of the `Authorization` header is zero.")
)
// Context is the context of the JSON web token.
type Context struct {
ID uint64
Username string
}
// secretFunc validates the secret format.
func secretFunc(secret string) jwt.Keyfunc {
return func(token *jwt.Token) (interface{}, error) {
// Make sure the `alg` is what we except.
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, jwt.ErrSignatureInvalid
}
return []byte(secret), nil
}
}
// Parse validates the token with the specified secret,
// and returns the context if the token was valid.
func Parse(tokenString string, secret string) (*Context, error) {
ctx := &Context{}
// Parse the token.
token, err := jwt.Parse(tokenString, secretFunc(secret))
// Parse error.
if err != nil {
return ctx, err
// Read the token if it's valid.
} else if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
ctx.ID = uint64(claims["id"].(float64))
ctx.Username = claims["username"].(string)
return ctx, nil
// Other errors.
} else {
return ctx, err
}
}
// ParseRequest gets the token from the header and
// pass it to the Parse function to parses the token.
func ParseRequest(c *gin.Context) (*Context, error) {
header := c.Request.Header.Get("Authorization")
// Load the jwt secret from config
secret := viper.GetString("jwt_secret")
if len(header) == 0 {
return &Context{}, ErrMissingHeader
}
var t string
// Parse the header to get the token part.
fmt.Sscanf(header, "Bearer %s", &t)
return Parse(t, secret)
}
// Sign signs the context with the specified secret.
func Sign(ctx *gin.Context, c Context, secret string) (tokenString string, err error) {
// Load the jwt secret from the Gin config if the secret isn't specified.
if secret == "" {
secret = viper.GetString("jwt_secret")
}
// The token content.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"id": c.ID,
"username": c.Username,
"nbf": time.Now().Unix(),
"iat": time.Now().Unix(),
})
// Sign the token with the specified secret.
tokenString, err = token.SignedString([]byte(secret))
return
}
================================================
FILE: demo09/router/middleware/auth.go
================================================
package middleware
import (
"apiserver/handler"
"apiserver/pkg/errno"
"apiserver/pkg/token"
"github.com/gin-gonic/gin"
)
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Parse the json web token.
if _, err := token.ParseRequest(c); err != nil {
handler.SendResponse(c, errno.ErrTokenInvalid, nil)
c.Abort()
return
}
c.Next()
}
}
================================================
FILE: demo09/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo09/router/middleware/logging.go
================================================
package middleware
import (
"bytes"
"encoding/json"
"io/ioutil"
"regexp"
"time"
"apiserver/handler"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/willf/pad"
)
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
// Logging is a middleware function that logs the each request.
func Logging() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now().UTC()
path := c.Request.URL.Path
reg := regexp.MustCompile("(/v1/user|/login)")
if !reg.MatchString(path) {
return
}
// Skip for the health check requests.
if path == "/sd/health" || path == "/sd/ram" || path == "/sd/cpu" || path == "/sd/disk" {
return
}
// Read the Body content
var bodyBytes []byte
if c.Request.Body != nil {
bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
}
// Restore the io.ReadCloser to its original state
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
// The basic informations.
method := c.Request.Method
ip := c.ClientIP()
//log.Debugf("New request come in, path: %s, Method: %s, body `%s`", path, method, string(bodyBytes))
blw := &bodyLogWriter{
body: bytes.NewBufferString(""),
ResponseWriter: c.Writer,
}
c.Writer = blw
// Continue.
c.Next()
// Calculates the latency.
end := time.Now().UTC()
latency := end.Sub(start)
code, message := -1, ""
// get code and message
var response handler.Response
if err := json.Unmarshal(blw.body.Bytes(), &response); err != nil {
log.Errorf(err, "response body can not unmarshal to model.Response struct, body: `%s`", blw.body.Bytes())
code = errno.InternalServerError.Code
message = err.Error()
} else {
code = response.Code
message = response.Message
}
log.Infof("%-13s | %-12s | %s %s | {code: %d, message: %s}", latency, ip, pad.Right(method, 5, ""), path, code, message)
}
}
================================================
FILE: demo09/router/middleware/requestid.go
================================================
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/satori/go.uuid"
)
func RequestId() gin.HandlerFunc {
return func(c *gin.Context) {
// Check for incoming header, use it if exists
requestId := c.Request.Header.Get("X-Request-Id")
// Create request id with UUID4
if requestId == "" {
u4, _ := uuid.NewV4()
requestId = u4.String()
}
// Expose it for use in the application
c.Set("X-Request-Id", requestId)
// Set X-Request-Id header
c.Writer.Header().Set("X-Request-Id", requestId)
c.Next()
}
}
================================================
FILE: demo09/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/handler/user"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
// api for authentication functionalities
g.POST("/login", user.Login)
// The user handlers, requiring authentication
u := g.Group("/v1/user")
u.Use(middleware.AuthMiddleware())
{
u.POST("", user.Create)
u.DELETE("/:id", user.Delete)
u.PUT("/:id", user.Update)
u.GET("", user.List)
u.GET("/:username", user.Get)
}
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo09/service/service.go
================================================
package service
import (
"fmt"
"sync"
"apiserver/model"
"apiserver/util"
)
func ListUser(username string, offset, limit int) ([]*model.UserInfo, uint64, error) {
infos := make([]*model.UserInfo, 0)
users, count, err := model.ListUser(username, offset, limit)
if err != nil {
return nil, count, err
}
ids := []uint64{}
for _, user := range users {
ids = append(ids, user.Id)
}
wg := sync.WaitGroup{}
userList := model.UserList{
Lock: new(sync.Mutex),
IdMap: make(map[uint64]*model.UserInfo, len(users)),
}
errChan := make(chan error, 1)
finished := make(chan bool, 1)
// Improve query efficiency in parallel
for _, u := range users {
wg.Add(1)
go func(u *model.UserModel) {
defer wg.Done()
shortId, err := util.GenShortId()
if err != nil {
errChan <- err
return
}
userList.Lock.Lock()
defer userList.Lock.Unlock()
userList.IdMap[u.Id] = &model.UserInfo{
Id: u.Id,
Username: u.Username,
SayHello: fmt.Sprintf("Hello %s", shortId),
Password: u.Password,
CreatedAt: u.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: u.UpdatedAt.Format("2006-01-02 15:04:05"),
}
}(u)
}
go func() {
wg.Wait()
close(finished)
}()
select {
case <-finished:
case err := <-errChan:
return nil, count, err
}
for _, id := range ids {
infos = append(infos, userList.IdMap[id])
}
return infos, count, nil
}
================================================
FILE: demo09/util/util.go
================================================
package util
import (
"github.com/gin-gonic/gin"
"github.com/teris-io/shortid"
)
func GenShortId() (string, error) {
return shortid.Generate()
}
func GetReqID(c *gin.Context) string {
v, ok := c.Get("X-Request-Id")
if !ok {
return ""
}
if requestId, ok := v.(string); ok {
return requestId
}
return ""
}
================================================
FILE: demo10/README.md
================================================
进阶:用HTTPS加密API请求
================================================
FILE: demo10/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
jwt_secret: Rtg8BPKNEf2mB4mgvKONGPZZQSaJWNLijxR42qRgq0iBb5
tls:
addr: :8081
cert: conf/server.crt
key: conf/server.key
log:
writers: stdout
logger_level: DEBUG
logger_file: log/apiserver.log
log_format_text: true
rollingPolicy: size
log_rotate_date: 1
log_rotate_size: 1
log_backup_count: 7
db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
docker_db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
================================================
FILE: demo10/conf/server.crt
================================================
-----BEGIN CERTIFICATE-----
MIID2TCCAsGgAwIBAgIJAMQRszo28YxYMA0GCSqGSIb3DQEBBQUAMIGCMQswCQYD
VQQGEwJERTEMMAoGA1UECAwDTlJXMQ4wDAYDVQQHDAVFYXJ0aDEXMBUGA1UECgwO
UmFuZG9tIENvbXBhbnkxCzAJBgNVBAsMAklUMRIwEAYDVQQDDAkxMjcuMC4wLjEx
GzAZBgkqhkiG9w0BCQEWDHh4eHh4QHFxLmNvbTAeFw0xODA2MDQwNTI5MzdaFw0y
ODA2MDEwNTI5MzdaMIGCMQswCQYDVQQGEwJERTEMMAoGA1UECAwDTlJXMQ4wDAYD
VQQHDAVFYXJ0aDEXMBUGA1UECgwOUmFuZG9tIENvbXBhbnkxCzAJBgNVBAsMAklU
MRIwEAYDVQQDDAkxMjcuMC4wLjExGzAZBgkqhkiG9w0BCQEWDHh4eHh4QHFxLmNv
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALw6CzJlFxNg79rSbFa/
Qn1IThDOb+MKOOpQjeWCSrKFTrWnU36BouUN7b9VYEn9+GpcFoyaThK5HsJX4wyd
65yTCct3WieuegdpbttA5w8vcHbUNwvfk9WTBbxXbweZrVajsGRhg1Y+8B2UM/DG
dzwrkO3woKaTrYlo7kkVFegODUhE7+KkEt/B9hiLf8WWk9fDjh8VVq0mXRQXhuIR
OfJ1UFb7CEdAoQa99wS0sc2hYW4QAkElgVbCAK+w2H1sPl48xaIVhsgeS/4BR1Om
EsykygsFtU8jwO4J/c/e8seekNFPssUBc/kFHQXp333H/zMhFLQfYcEZW/wPTV8R
C2ECAwEAAaNQME4wHQYDVR0OBBYEFCSzcWsMsL3gGOUnlWxSUZZOrud3MB8GA1Ud
IwQYMBaAFCSzcWsMsL3gGOUnlWxSUZZOrud3MAwGA1UdEwQFMAMBAf8wDQYJKoZI
hvcNAQEFBQADggEBAK8GS3jy4Og6WHB68BPCc2odBjAlug91otn0PNtpfp431fWw
bpviOqRtr6aFqJ8SwdVtWynjAXTTeKZNJB0XNf7HnPMC15A+L4MwtUH9SPQ3abeW
XLQTGl0Q5sgLPR9jADQ3gEgoO4cgKMNy97YhemkQZ1PpVhH9VGObebObcpPRvtQL
5+TurehVL1hYsrQ5e6q1VUZg/DJuYT096t238vYU5GimLgv8d4GSXA/PbAeTBzd6
1mofLFAyp2eJkjOZgo8OgNVSQ3aRqUluoD/8qSjnBhr9GoANCuBFJMphqPqMxYtZ
cBM/rpG8PFiF5gCjeU/6rt+39w9QEST7y583dVk=
-----END CERTIFICATE-----
================================================
FILE: demo10/conf/server.key
================================================
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8OgsyZRcTYO/a
0mxWv0J9SE4Qzm/jCjjqUI3lgkqyhU61p1N+gaLlDe2/VWBJ/fhqXBaMmk4SuR7C
V+MMneuckwnLd1onrnoHaW7bQOcPL3B21DcL35PVkwW8V28Hma1Wo7BkYYNWPvAd
lDPwxnc8K5Dt8KCmk62JaO5JFRXoDg1IRO/ipBLfwfYYi3/FlpPXw44fFVatJl0U
F4biETnydVBW+whHQKEGvfcEtLHNoWFuEAJBJYFWwgCvsNh9bD5ePMWiFYbIHkv+
AUdTphLMpMoLBbVPI8DuCf3P3vLHnpDRT7LFAXP5BR0F6d99x/8zIRS0H2HBGVv8
D01fEQthAgMBAAECggEAVjU/a5VhPD7pnA9ED3cJvNeg6ZGjLRlBeA/s7XD/RURJ
CGnak9ZMBRycB0XTFBB99ji3Gy6RE4I11EzscJrjjpLJqabAY+xFd5+SZlkTeqD/
oW0QyR9dVjRALELfV1vLSCMwZslCnf21e9ak82Hyulw5xMCw05pPoN+uQ0qk/eKn
iOPnWhMLg2o2XX1C4ScYOVF0lY9RVyOIGEl6pW97QOlNM3M2D02iUytgg44Y0DSU
q2vo2dcroNGzr8vEb1JzFbiXBeG1EBKWhB9boswhZ78Wer+KWwWYaoJcK6BgHRq+
MPRU36LarxLT47reoq1yeN5fM3NmaYiGKroHbGPwAQKBgQDzipn70+HIOU+vXyoa
6eQJDOfTgXjtEh1jbozus+PIgYo1ciFo9awmRGvDelItF2zOtToCU7AmzM/XNhiU
QNQvoV5fYRUfou0wU9m/BtiOi2rzOWgZmPsuc4tO5ptk7lNuqfaJ9b3pH0ie4UgO
WA0U2gwvZOG9hUbJU5YaFpwbCQKBgQDF2wuzql8sb/Ml6G2sGqg6RvRyO1N71hS3
oqmLxY/2Nr9RO6KvIGw1tAMoxPAyB/erjs0X5UVF3XuKE4bmZMqHBrsVe3f8SA97
igk9gFGTGI51tiLADnLp8uXKW1/Wd6GwhM1fxPagiASExhNU/TwZgmwGdDMMyeMW
UJdW+x+LmQKBgQCVVqljxaKOv64AUO+lv0SI1DQX+y2m2dPRlAmxmfeUjPKuIUUh
cnxUnuIh5REc+19KRdDDeoPq1u6f/lkGF9bFOkN/Yy2rz6F4YAKG4/DJP+6eJNaT
07461rlW8YvaUVYx5uD56gnBOOC0JFqmCRJEdgzAxzCxoVctvyas6q5g2QKBgB3p
g9dhxom9UxFEFnCShyRoXcR3W6O5NeCdYuySrbUXic0KKwo26KUl1eRwAbBOrA7v
w+n864Aof+jcEuT6D/Rh/B6/T+CANHcE42i84ZhPehopsw8+H/lmk38IWXDfHT7G
lRYJfQ/AAI7iM0ICFvf0U8iWALHKQ963yGmKBbbhAoGBALTA/DHcxzNummVrvZh2
0jlWOBySt3q9Nq5umsT3Q27cfgubqnxJDr574xlw2rFay07FdinSDb4alQUz7k4E
he3IfqcBzUmEsw4fL0uC/KOj1kHbIQVEstZtFBS2e3mxlEm+xPQCNWB45enWmzyb
lS3DYiIrrNZ95tjw4FYiwlw2
-----END PRIVATE KEY-----
================================================
FILE: demo10/config/config.go
================================================
package config
import (
"strings"
"github.com/fsnotify/fsnotify"
"github.com/lexkong/log"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 初始化日志包
c.initLog()
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
func (c *Config) initLog() {
passLagerCfg := log.PassLagerCfg{
Writers: viper.GetString("log.writers"),
LoggerLevel: viper.GetString("log.logger_level"),
LoggerFile: viper.GetString("log.logger_file"),
LogFormatText: viper.GetBool("log.log_format_text"),
RollingPolicy: viper.GetString("log.rollingPolicy"),
LogRotateDate: viper.GetInt("log.log_rotate_date"),
LogRotateSize: viper.GetInt("log.log_rotate_size"),
LogBackupCount: viper.GetInt("log.log_backup_count"),
}
log.InitWithConfig(&passLagerCfg)
}
// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Infof("Config file changed: %s", e.Name)
})
}
================================================
FILE: demo10/db.sql
================================================
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `db_apiserver` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `db_apiserver`;
--
-- Table structure for table `tb_users`
--
DROP TABLE IF EXISTS `tb_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tb_users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`createdAt` timestamp NULL DEFAULT NULL,
`updatedAt` timestamp NULL DEFAULT NULL,
`deletedAt` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
KEY `idx_tb_users_deletedAt` (`deletedAt`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tb_users`
--
LOCK TABLES `tb_users` WRITE;
/*!40000 ALTER TABLE `tb_users` DISABLE KEYS */;
INSERT INTO `tb_users` VALUES (0,'admin','$2a$10$veGcArz47VGj7l9xN7g2iuT9TF21jLI1YGXarGzvARNdnt4inC9PG','2018-05-27 16:25:33','2018-05-27 16:25:33',NULL);
/*!40000 ALTER TABLE `tb_users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2018-05-28 0:25:41
================================================
FILE: demo10/handler/handler.go
================================================
package handler
import (
"net/http"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func SendResponse(c *gin.Context, err error, data interface{}) {
code, message := errno.DecodeErr(err)
// always return http.StatusOK
c.JSON(http.StatusOK, Response{
Code: code,
Message: message,
Data: data,
})
}
================================================
FILE: demo10/handler/sd/check.go
================================================
package sd
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
)
const (
B = 1
KB = 1024 * B
MB = 1024 * KB
GB = 1024 * MB
)
// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
message := "OK"
c.String(http.StatusOK, "\n"+message)
}
// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
u, _ := disk.Usage("/")
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusOK
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
cores, _ := cpu.Counts(false)
a, _ := load.Avg()
l1 := a.Load1
l5 := a.Load5
l15 := a.Load15
status := http.StatusOK
text := "OK"
if l5 >= float64(cores-1) {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if l5 >= float64(cores-2) {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
c.String(status, "\n"+message)
}
// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
u, _ := mem.VirtualMemory()
usedMB := int(u.Used) / MB
usedGB := int(u.Used) / GB
totalMB := int(u.Total) / MB
totalGB := int(u.Total) / GB
usedPercent := int(u.UsedPercent)
status := http.StatusOK
text := "OK"
if usedPercent >= 95 {
status = http.StatusInternalServerError
text = "CRITICAL"
} else if usedPercent >= 90 {
status = http.StatusTooManyRequests
text = "WARNING"
}
message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
c.String(status, "\n"+message)
}
================================================
FILE: demo10/handler/user/create.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"apiserver/util"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/lexkong/log/lager"
)
// Create creates a new user account.
func Create(c *gin.Context) {
log.Info("User Create function called.", lager.Data{"X-Request-Id": util.GetReqID(c)})
var r CreateRequest
if err := c.Bind(&r); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
u := model.UserModel{
Username: r.Username,
Password: r.Password,
}
// Validate the data.
if err := u.Validate(); err != nil {
SendResponse(c, errno.ErrValidation, nil)
return
}
// Encrypt the user password.
if err := u.Encrypt(); err != nil {
SendResponse(c, errno.ErrEncrypt, nil)
return
}
// Insert the user to the database.
if err := u.Create(); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
rsp := CreateResponse{
Username: r.Username,
}
// Show the user information.
SendResponse(c, nil, rsp)
}
================================================
FILE: demo10/handler/user/delete.go
================================================
package user
import (
"strconv"
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
// Delete delete an user by the user identifier.
func Delete(c *gin.Context) {
userId, _ := strconv.Atoi(c.Param("id"))
if err := model.DeleteUser(uint64(userId)); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
SendResponse(c, nil, nil)
}
================================================
FILE: demo10/handler/user/get.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
)
// Get gets an user by the user identifier.
func Get(c *gin.Context) {
username := c.Param("username")
// Get the user by the `username` from the database.
user, err := model.GetUser(username)
if err != nil {
SendResponse(c, errno.ErrUserNotFound, nil)
return
}
SendResponse(c, nil, user)
}
================================================
FILE: demo10/handler/user/list.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/pkg/errno"
"apiserver/service"
"github.com/gin-gonic/gin"
)
// List list the users in the database.
func List(c *gin.Context) {
var r ListRequest
if err := c.Bind(&r); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
infos, count, err := service.ListUser(r.Username, r.Offset, r.Limit)
if err != nil {
SendResponse(c, err, nil)
return
}
SendResponse(c, nil, ListResponse{
TotalCount: count,
UserList: infos,
})
}
================================================
FILE: demo10/handler/user/login.go
================================================
package user
import (
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/auth"
"apiserver/pkg/errno"
"apiserver/pkg/token"
"github.com/gin-gonic/gin"
)
// Login generates the authentication token
// if the password was matched with the specified account.
func Login(c *gin.Context) {
// Binding the data with the user struct.
var u model.UserModel
if err := c.Bind(&u); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
// Get the user information by the login username.
d, err := model.GetUser(u.Username)
if err != nil {
SendResponse(c, errno.ErrUserNotFound, nil)
return
}
// Compare the login password with the user password.
if err := auth.Compare(d.Password, u.Password); err != nil {
SendResponse(c, errno.ErrPasswordIncorrect, nil)
return
}
// Sign the json web token.
t, err := token.Sign(c, token.Context{ID: d.Id, Username: d.Username}, "")
if err != nil {
SendResponse(c, errno.ErrToken, nil)
return
}
SendResponse(c, nil, model.Token{Token: t})
}
================================================
FILE: demo10/handler/user/update.go
================================================
package user
import (
"strconv"
. "apiserver/handler"
"apiserver/model"
"apiserver/pkg/errno"
"apiserver/util"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/lexkong/log/lager"
)
// Update update a exist user account info.
func Update(c *gin.Context) {
log.Info("Update function called.", lager.Data{"X-Request-Id": util.GetReqID(c)})
// Get the user id from the url parameter.
userId, _ := strconv.Atoi(c.Param("id"))
// Binding the user data.
var u model.UserModel
if err := c.Bind(&u); err != nil {
SendResponse(c, errno.ErrBind, nil)
return
}
// We update the record based on the user id.
u.Id = uint64(userId)
// Validate the data.
if err := u.Validate(); err != nil {
SendResponse(c, errno.ErrValidation, nil)
return
}
// Encrypt the user password.
if err := u.Encrypt(); err != nil {
SendResponse(c, errno.ErrEncrypt, nil)
return
}
// Save changed fields.
if err := u.Update(); err != nil {
SendResponse(c, errno.ErrDatabase, nil)
return
}
SendResponse(c, nil, nil)
}
================================================
FILE: demo10/handler/user/user.go
================================================
package user
import (
"apiserver/model"
)
type CreateRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type CreateResponse struct {
Username string `json:"username"`
}
type ListRequest struct {
Username string `json:"username"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
type ListResponse struct {
TotalCount uint64 `json:"totalCount"`
UserList []*model.UserInfo `json:"userList"`
}
================================================
FILE: demo10/main.go
================================================
package main
import (
"errors"
"net/http"
"time"
"apiserver/config"
"apiserver/model"
"apiserver/router"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
pflag.Parse()
// init config
if err := config.Init(*cfg); err != nil {
panic(err)
}
// init db
model.DB.Init()
defer model.DB.Close()
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
// Create the Gin engine.
g := gin.New()
// Routes.
router.Load(
// Cores.
g,
// Middlwares.
middleware.Logging(),
middleware.RequestId(),
)
// Ping the server to make sure the router is working.
go func() {
if err := pingServer(); err != nil {
log.Fatal("The router has no response, or it might took too long to start up.", err)
}
log.Info("The router has been deployed successfully.")
}()
// Start to listening the incoming requests.
cert := viper.GetString("tls.cert")
key := viper.GetString("tls.key")
if cert != "" && key != "" {
go func() {
log.Infof("Start to listening the incoming requests on https address: %s", viper.GetString("tls.addr"))
log.Info(http.ListenAndServeTLS(viper.GetString("tls.addr"), cert, key, g).Error())
}()
}
log.Infof("Start to listening the incoming requests on http address: %s", viper.GetString("addr"))
log.Info(http.ListenAndServe(viper.GetString("addr"), g).Error())
}
// pingServer pings the http server to make sure the router is working.
func pingServer() error {
for i := 0; i < viper.GetInt("max_ping_count"); i++ {
// Ping the server by sending a GET request to `/health`.
resp, err := http.Get(viper.GetString("url") + "/sd/health")
if err == nil && resp.StatusCode == 200 {
return nil
}
// Sleep for a second to continue the next ping.
log.Info("Waiting for the router, retry in 1 second.")
time.Sleep(time.Second)
}
return errors.New("Cannot connect to the router.")
}
================================================
FILE: demo10/model/init.go
================================================
package model
import (
"fmt"
"github.com/lexkong/log"
"github.com/spf13/viper"
// MySQL driver.
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type Database struct {
Self *gorm.DB
Docker *gorm.DB
}
var DB *Database
func openDB(username, password, addr, name string) *gorm.DB {
config := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
username,
password,
addr,
name,
true,
//"Asia/Shanghai"),
"Local")
db, err := gorm.Open("mysql", config)
if err != nil {
log.Errorf(err, "Database connection failed. Database name: %s", name)
}
// set for db connection
setupDB(db)
return db
}
func setupDB(db *gorm.DB) {
db.LogMode(viper.GetBool("gormlog"))
//db.DB().SetMaxOpenConns(20000) // 用于设置最大打开的连接数,默认值为0表示不限制.设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。
db.DB().SetMaxIdleConns(0) // 用于设置闲置的连接数.设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。
}
// used for cli
func InitSelfDB() *gorm.DB {
return openDB(viper.GetString("db.username"),
viper.GetString("db.password"),
viper.GetString("db.addr"),
viper.GetString("db.name"))
}
func GetSelfDB() *gorm.DB {
return InitSelfDB()
}
func InitDockerDB() *gorm.DB {
return openDB(viper.GetString("docker_db.username"),
viper.GetString("docker_db.password"),
viper.GetString("docker_db.addr"),
viper.GetString("docker_db.name"))
}
func GetDockerDB() *gorm.DB {
return InitDockerDB()
}
func (db *Database) Init() {
DB = &Database{
Self: GetSelfDB(),
Docker: GetDockerDB(),
}
}
func (db *Database) Close() {
DB.Self.Close()
DB.Docker.Close()
}
================================================
FILE: demo10/model/model.go
================================================
package model
import (
"sync"
"time"
)
type BaseModel struct {
Id uint64 `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"-"`
CreatedAt time.Time `gorm:"column:createdAt" json:"-"`
UpdatedAt time.Time `gorm:"column:updatedAt" json:"-"`
DeletedAt *time.Time `gorm:"column:deletedAt" sql:"index" json:"-"`
}
type UserInfo struct {
Id uint64 `json:"id"`
Username string `json:"username"`
SayHello string `json:"sayHello"`
Password string `json:"password"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type UserList struct {
Lock *sync.Mutex
IdMap map[uint64]*UserInfo
}
// Token represents a JSON web token.
type Token struct {
Token string `json:"token"`
}
================================================
FILE: demo10/model/user.go
================================================
package model
import (
"fmt"
"apiserver/pkg/auth"
"apiserver/pkg/constvar"
validator "gopkg.in/go-playground/validator.v9"
)
// User represents a registered user.
type UserModel struct {
BaseModel
Username string `json:"username" gorm:"column:username;not null" binding:"required" validate:"min=1,max=32"`
Password string `json:"password" gorm:"column:password;not null" binding:"required" validate:"min=5,max=128"`
}
func (c *UserModel) TableName() string {
return "tb_users"
}
// Create creates a new user account.
func (u *UserModel) Create() error {
return DB.Self.Create(&u).Error
}
// DeleteUser deletes the user by the user identifier.
func DeleteUser(id uint64) error {
user := UserModel{}
user.BaseModel.Id = id
return DB.Self.Delete(&user).Error
}
// Update updates an user account information.
func (u *UserModel) Update() error {
return DB.Self.Save(u).Error
}
// GetUser gets an user by the user identifier.
func GetUser(username string) (*UserModel, error) {
u := &UserModel{}
d := DB.Self.Where("username = ?", username).First(&u)
return u, d.Error
}
// ListUser List all users
func ListUser(username string, offset, limit int) ([]*UserModel, uint64, error) {
if limit == 0 {
limit = constvar.DefaultLimit
}
users := make([]*UserModel, 0)
var count uint64
where := fmt.Sprintf("username like '%%%s%%'", username)
if err := DB.Self.Model(&UserModel{}).Where(where).Count(&count).Error; err != nil {
return users, count, err
}
if err := DB.Self.Where(where).Offset(offset).Limit(limit).Order("id desc").Find(&users).Error; err != nil {
return users, count, err
}
return users, count, nil
}
// Compare with the plain text password. Returns true if it's the same as the encrypted one (in the `User` struct).
func (u *UserModel) Compare(pwd string) (err error) {
err = auth.Compare(u.Password, pwd)
return
}
// Encrypt the user password.
func (u *UserModel) Encrypt() (err error) {
u.Password, err = auth.Encrypt(u.Password)
return
}
// Validate the fields.
func (u *UserModel) Validate() error {
validate := validator.New()
return validate.Struct(u)
}
================================================
FILE: demo10/pkg/auth/auth.go
================================================
package auth
import "golang.org/x/crypto/bcrypt"
// Encrypt encrypts the plain text with bcrypt.
func Encrypt(source string) (string, error) {
hashedBytes, err := bcrypt.GenerateFromPassword([]byte(source), bcrypt.DefaultCost)
return string(hashedBytes), err
}
// Compare compares the encrypted text with the plain text if it's the same.
func Compare(hashedPassword, password string) error {
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}
================================================
FILE: demo10/pkg/constvar/constvar.go
================================================
package constvar
const (
DefaultLimit = 50
)
================================================
FILE: demo10/pkg/errno/code.go
================================================
package errno
var (
// Common errors
OK = &Errno{Code: 0, Message: "OK"}
InternalServerError = &Errno{Code: 10001, Message: "Internal server error"}
ErrBind = &Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct."}
ErrValidation = &Errno{Code: 20001, Message: "Validation failed."}
ErrDatabase = &Errno{Code: 20002, Message: "Database error."}
ErrToken = &Errno{Code: 20003, Message: "Error occurred while signing the JSON web token."}
// user errors
ErrEncrypt = &Errno{Code: 20101, Message: "Error occurred while encrypting the user password."}
ErrUserNotFound = &Errno{Code: 20102, Message: "The user was not found."}
ErrTokenInvalid = &Errno{Code: 20103, Message: "The token was invalid."}
ErrPasswordIncorrect = &Errno{Code: 20104, Message: "The password was incorrect."}
)
================================================
FILE: demo10/pkg/errno/errno.go
================================================
package errno
import "fmt"
type Errno struct {
Code int
Message string
}
func (err Errno) Error() string {
return err.Message
}
// Err represents an error
type Err struct {
Code int
Message string
Err error
}
func New(errno *Errno, err error) *Err {
return &Err{Code: errno.Code, Message: errno.Message, Err: err}
}
func (err *Err) Add(message string) error {
err.Message += " " + message
return err
}
func (err *Err) Addf(format string, args ...interface{}) error {
err.Message += " " + fmt.Sprintf(format, args...)
return err
}
func (err *Err) Error() string {
return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err)
}
func IsErrUserNotFound(err error) bool {
code, _ := DecodeErr(err)
return code == ErrUserNotFound.Code
}
func DecodeErr(err error) (int, string) {
if err == nil {
return OK.Code, OK.Message
}
switch typed := err.(type) {
case *Err:
return typed.Code, typed.Message
case *Errno:
return typed.Code, typed.Message
default:
}
return InternalServerError.Code, err.Error()
}
================================================
FILE: demo10/pkg/token/token.go
================================================
package token
import (
"errors"
"fmt"
"time"
jwt "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
var (
// ErrMissingHeader means the `Authorization` header was empty.
ErrMissingHeader = errors.New("The length of the `Authorization` header is zero.")
)
// Context is the context of the JSON web token.
type Context struct {
ID uint64
Username string
}
// secretFunc validates the secret format.
func secretFunc(secret string) jwt.Keyfunc {
return func(token *jwt.Token) (interface{}, error) {
// Make sure the `alg` is what we except.
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, jwt.ErrSignatureInvalid
}
return []byte(secret), nil
}
}
// Parse validates the token with the specified secret,
// and returns the context if the token was valid.
func Parse(tokenString string, secret string) (*Context, error) {
ctx := &Context{}
// Parse the token.
token, err := jwt.Parse(tokenString, secretFunc(secret))
// Parse error.
if err != nil {
return ctx, err
// Read the token if it's valid.
} else if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
ctx.ID = uint64(claims["id"].(float64))
ctx.Username = claims["username"].(string)
return ctx, nil
// Other errors.
} else {
return ctx, err
}
}
// ParseRequest gets the token from the header and
// pass it to the Parse function to parses the token.
func ParseRequest(c *gin.Context) (*Context, error) {
header := c.Request.Header.Get("Authorization")
// Load the jwt secret from config
secret := viper.GetString("jwt_secret")
if len(header) == 0 {
return &Context{}, ErrMissingHeader
}
var t string
// Parse the header to get the token part.
fmt.Sscanf(header, "Bearer %s", &t)
return Parse(t, secret)
}
// Sign signs the context with the specified secret.
func Sign(ctx *gin.Context, c Context, secret string) (tokenString string, err error) {
// Load the jwt secret from the Gin config if the secret isn't specified.
if secret == "" {
secret = viper.GetString("jwt_secret")
}
// The token content.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"id": c.ID,
"username": c.Username,
"nbf": time.Now().Unix(),
"iat": time.Now().Unix(),
})
// Sign the token with the specified secret.
tokenString, err = token.SignedString([]byte(secret))
return
}
================================================
FILE: demo10/router/middleware/auth.go
================================================
package middleware
import (
"apiserver/handler"
"apiserver/pkg/errno"
"apiserver/pkg/token"
"github.com/gin-gonic/gin"
)
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Parse the json web token.
if _, err := token.ParseRequest(c); err != nil {
handler.SendResponse(c, errno.ErrTokenInvalid, nil)
c.Abort()
return
}
c.Next()
}
}
================================================
FILE: demo10/router/middleware/header.go
================================================
package middleware
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
c.Next()
}
// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
c.Header("Content-Type", "application/json")
c.AbortWithStatus(200)
}
}
// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("X-Frame-Options", "DENY")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-XSS-Protection", "1; mode=block")
if c.Request.TLS != nil {
c.Header("Strict-Transport-Security", "max-age=31536000")
}
// Also consider adding Content-Security-Policy headers
// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
================================================
FILE: demo10/router/middleware/logging.go
================================================
package middleware
import (
"bytes"
"encoding/json"
"io/ioutil"
"regexp"
"time"
"apiserver/handler"
"apiserver/pkg/errno"
"github.com/gin-gonic/gin"
"github.com/lexkong/log"
"github.com/willf/pad"
)
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
// Logging is a middleware function that logs the each request.
func Logging() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now().UTC()
path := c.Request.URL.Path
reg := regexp.MustCompile("(/v1/user|/login)")
if !reg.MatchString(path) {
return
}
// Skip for the health check requests.
if path == "/sd/health" || path == "/sd/ram" || path == "/sd/cpu" || path == "/sd/disk" {
return
}
// Read the Body content
var bodyBytes []byte
if c.Request.Body != nil {
bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
}
// Restore the io.ReadCloser to its original state
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
// The basic informations.
method := c.Request.Method
ip := c.ClientIP()
//log.Debugf("New request come in, path: %s, Method: %s, body `%s`", path, method, string(bodyBytes))
blw := &bodyLogWriter{
body: bytes.NewBufferString(""),
ResponseWriter: c.Writer,
}
c.Writer = blw
// Continue.
c.Next()
// Calculates the latency.
end := time.Now().UTC()
latency := end.Sub(start)
code, message := -1, ""
// get code and message
var response handler.Response
if err := json.Unmarshal(blw.body.Bytes(), &response); err != nil {
log.Errorf(err, "response body can not unmarshal to model.Response struct, body: `%s`", blw.body.Bytes())
code = errno.InternalServerError.Code
message = err.Error()
} else {
code = response.Code
message = response.Message
}
log.Infof("%-13s | %-12s | %s %s | {code: %d, message: %s}", latency, ip, pad.Right(method, 5, ""), path, code, message)
}
}
================================================
FILE: demo10/router/middleware/requestid.go
================================================
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/satori/go.uuid"
)
func RequestId() gin.HandlerFunc {
return func(c *gin.Context) {
// Check for incoming header, use it if exists
requestId := c.Request.Header.Get("X-Request-Id")
// Create request id with UUID4
if requestId == "" {
u4, _ := uuid.NewV4()
requestId = u4.String()
}
// Expose it for use in the application
c.Set("X-Request-Id", requestId)
// Set X-Request-Id header
c.Writer.Header().Set("X-Request-Id", requestId)
c.Next()
}
}
================================================
FILE: demo10/router/router.go
================================================
package router
import (
"net/http"
"apiserver/handler/sd"
"apiserver/handler/user"
"apiserver/router/middleware"
"github.com/gin-gonic/gin"
)
// Load loads the middlewares, routes, handlers.
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
// Middlewares.
g.Use(gin.Recovery())
g.Use(middleware.NoCache)
g.Use(middleware.Options)
g.Use(middleware.Secure)
g.Use(mw...)
// 404 Handler.
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route.")
})
// api for authentication functionalities
g.POST("/login", user.Login)
// The user handlers, requiring authentication
u := g.Group("/v1/user")
u.Use(middleware.AuthMiddleware())
{
u.POST("", user.Create)
u.DELETE("/:id", user.Delete)
u.PUT("/:id", user.Update)
u.GET("", user.List)
u.GET("/:username", user.Get)
}
// The health check handlers
svcd := g.Group("/sd")
{
svcd.GET("/health", sd.HealthCheck)
svcd.GET("/disk", sd.DiskCheck)
svcd.GET("/cpu", sd.CPUCheck)
svcd.GET("/ram", sd.RAMCheck)
}
return g
}
================================================
FILE: demo10/service/service.go
================================================
package service
import (
"fmt"
"sync"
"apiserver/model"
"apiserver/util"
)
func ListUser(username string, offset, limit int) ([]*model.UserInfo, uint64, error) {
infos := make([]*model.UserInfo, 0)
users, count, err := model.ListUser(username, offset, limit)
if err != nil {
return nil, count, err
}
ids := []uint64{}
for _, user := range users {
ids = append(ids, user.Id)
}
wg := sync.WaitGroup{}
userList := model.UserList{
Lock: new(sync.Mutex),
IdMap: make(map[uint64]*model.UserInfo, len(users)),
}
errChan := make(chan error, 1)
finished := make(chan bool, 1)
// Improve query efficiency in parallel
for _, u := range users {
wg.Add(1)
go func(u *model.UserModel) {
defer wg.Done()
shortId, err := util.GenShortId()
if err != nil {
errChan <- err
return
}
userList.Lock.Lock()
defer userList.Lock.Unlock()
userList.IdMap[u.Id] = &model.UserInfo{
Id: u.Id,
Username: u.Username,
SayHello: fmt.Sprintf("Hello %s", shortId),
Password: u.Password,
CreatedAt: u.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: u.UpdatedAt.Format("2006-01-02 15:04:05"),
}
}(u)
}
go func() {
wg.Wait()
close(finished)
}()
select {
case <-finished:
case err := <-errChan:
return nil, count, err
}
for _, id := range ids {
infos = append(infos, userList.IdMap[id])
}
return infos, count, nil
}
================================================
FILE: demo10/util/util.go
================================================
package util
import (
"github.com/gin-gonic/gin"
"github.com/teris-io/shortid"
)
func GenShortId() (string, error) {
return shortid.Generate()
}
func GetReqID(c *gin.Context) string {
v, ok := c.Get("X-Request-Id")
if !ok {
return ""
}
if requestId, ok := v.(string); ok {
return requestId
}
return ""
}
================================================
FILE: demo11/Makefile
================================================
all: gotool
@go build -v .
clean:
rm -f apiserver
find . -name "[._]*.s[a-w][a-z]" | xargs -i rm -f {}
gotool:
gofmt -w .
go tool vet . |& grep -v vendor;true
ca:
openssl req -new -nodes -x509 -out conf/server.crt -keyout conf/server.key -days 3650 -subj "/C=DE/ST=NRW/L=Earth/O=Random Company/OU=IT/CN=127.0.0.1/emailAddress=xxxxx@qq.com"
help:
@echo "make - compile the source code"
@echo "make clean - remove binary file and vim swp files"
@echo "make gotool - run go tool 'fmt' and 'vet'"
@echo "make ca - generate ca files"
.PHONY: clean gotool ca help
================================================
FILE: demo11/README.md
================================================
进阶:用Makefile管理API项目
================================================
FILE: demo11/conf/config.yaml
================================================
runmode: debug # 开发模式, debug, release, test
addr: :8080 # HTTP绑定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8080 # pingServer函数请求的API服务器的ip:port
max_ping_count: 10 # pingServer函数try的次数
jwt_secret: Rtg8BPKNEf2mB4mgvKONGPZZQSaJWNLijxR42qRgq0iBb5
tls:
addr: :8081
cert: conf/server.crt
key: conf/server.key
log:
writers: stdout
logger_level: DEBUG
logger_file: log/apiserver.log
log_format_text: true
rollingPolicy: size
log_rotate_date: 1
log_rotate_size: 1
log_backup_count: 7
db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
docker_db:
name: db_apiserver
addr: 127.0.0.1:3306
username: root
password: root
================================================
FILE: demo11/conf/server.crt
================================================
-----BEGIN CERTIFICATE-----
MIID2TCCAsGgAwIBAgIJAI/8axTNy37vMA0GCSqGSIb3DQEBBQUAMIGCMQswCQYD
VQQGEwJERTEMMAoGA1UECAwDTlJXMQ4wDAYDVQQHDAVFYXJ0aDEXMBUGA1UECgwO
UmFuZG9tIENvbXBhbnkxCzAJBgNVBAsMAklUMRIwEAYDVQQDDAkxMjcuMC4wLjEx
GzAZBgkqhkiG9w0BCQEWDHh4eHh4QHFxLmNvbTAeFw0xODA2MDQwNjMwMzFaFw0y
ODA2MDEwNjMwMzFaMIGCMQswCQYDVQQGEwJERTEMMAoGA1UECAwDTlJXMQ4wDAYD
VQQHDAVFYXJ0aDEXMBUGA1UECgwOUmFuZG9tIENvbXBhbnkxCzAJBgNVBAsMAklU
MRIwEAYDVQQDDAkxMjcuMC4wLjExGzAZBgkqhkiG9w0BCQEWDHh4eHh4QHFxLmNv
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKHbnpmaxHokFC8rnJlG
42eL4C3tog7rffv96DrwDajdFNk8DeaNUpZ+R4X4YPecumsCsbol2dDC1IHHGJUN
q/A1vRjyDptMLmzp30p1aqVPcz+j8GMXtZUflLgup+HeKZRdrsk/0SaH1C3ywUBm
yDMNHUxEvoFj0x9aJoPutnkEk9miZt/kF2vxlTbKFM14/TecVr3ljNwTebJAqEw9
iaZpNXn/IQV2I2PTGlxjyT6XNsCchjMTJ9y/XS9rBeX6BFHgQsV52CW1mWz8FieM
/nyF9hE/TZKCpMtHWEYIdBbrxxY0BlhMxEtyIFzTha5zltFrQJGUYz7RIlyuabjk
or8CAwEAAaNQME4wHQYDVR0OBBYEFIjLBQlToqUDoyOLeUd6HeaqiiLJMB8GA1Ud
IwQYMBaAFIjLBQlToqUDoyOLeUd6HeaqiiLJMAwGA1UdEwQFMAMBAf8wDQYJKoZI
hvcNAQEFBQADggEBAHAqpqGNUNgYCr3JvrVULDqFKmqLpvDKfLd6kmtnFzvuZLSz
ANE2RXXqQ5Ms3F0oHqZQq4aOvgvfas5dBLoToPL/ip+GibQ7O/1PsPD5bZaczUel
AnmClX4ewcOEWmZx9mbYOz2C8RREvKU3MoBERrWA33LquYH0+8W8WHyMKNNF97oD
BgWZ8R1t3N59ySZyHcye7ddlJUubRbdyCGrHbmziHV3Cj61NJiwS2JRvhYkc71M6
8921j63qEM6i0NusFYG/w0GM3x/OeaeY/om6x+IOhJp3vIa1JPSekVyNSCwDEc3B
+8WmVFaeMMXwErSkuu8nCbqRP8J1jiKgzpG/92g=
-----END CERTIFICATE-----
================================================
FILE: demo11/conf/server.key
================================================
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCh256ZmsR6JBQv
K5yZRuNni+At7aIO6337/eg68A2o3RTZPA3mjVKWfkeF+GD3nLprArG6JdnQwtSB
xxiVDavwNb0Y8g6bTC5s6d9KdWqlT3M/o/BjF7WVH5S4Lqfh3imUXa7JP9Emh9Qt
8sFAZsgzDR1MRL6BY9MfWiaD7rZ5BJPZombf5Bdr8ZU2yhTNeP03nFa95YzcE3my
QKhMPYmmaTV5/yEFdiNj0xpcY8k+lzbAnIYzEyfcv10vawXl+gRR4ELFedgltZls
/BYnjP58hfYRP02SgqTLR1hGCHQW68cWNAZYTMRLciBc04Wuc5bRa0CRlGM+0SJc
rmm45KK/AgMBAAECggEASNMWvgf7pPT8u+iEchaKFLnDqQaFZu8f5TRtu67shnDK
g59YpcYqRZoVtjp17pLu8Vzp+FY1dY9jq+yXq+DV3qNfLI0kc01IiiqEE+1WiYCA
2z541y0Av1LRSDl9wcuCq8Wm8der1AlDN1VFDCPyqb2Z1AoOKQtwH2ghcjUCltn4
NxuMYOVdql+Idl/7PCEha9pW3DQvPqJVXRPvBh6tB7FFuUOIq4+EFRUWWZlyCJfQ
fYxB9jcFUWmAUieV3iBajg6H6QVkj/STLSgKuSktEBM2mqyL4slHXFWRt7+NQikg
tOEWnqcTC4Ei2Dyam4SBXHJ1wT8AJV4kMTrN1ZBRmQKBgQDOMcIGHyuBI1aHcvPW
Tkein7bslFAKsA1LBqnw+alWzSHMKEmfWbBjeW0FQTIGEYMXwIq5JhnNcS5Kb5Qt
BZgbBPDb7S80DtJnkno3iJ6hpijE3X/29lezigIuGibO5MD0Na1am2BP0iU77ifV
xpYvj/j/pkdcTLM7hRDImSeNIwKBgQDI9EUFkGi6H9NZEles+MCHenVRYUBT2PXe
hmtd9d5f4v3CmRsXvl4Dl/slIrJ9kuZktAck3Ocx6D0hnZcj1mwq077K7H3QqKCl
zRwB11H5v/imsZ1ey0hLVPhCzr2miRPM/9oL5B2st9eotzhyRcrMn+oYZSSt79S8
Q3YDiZ7TtQKBgENSr7z79GJnvVrgR4kTagRJDZrVGgVDUjPK6zXI7mdu9rgH93HW
AOeZv+TVUpX0pc7diO3G6OnRKIIZSFIi33UC+fl0ydK/fCdhBhKXwuOYsvsEL0Hd
UOlICEoxM7adrfqOhBlvXdTyEkItEkiUXHkPEwe1rNsQF/05By/YAbftAoGAYB2F
jd2+WZezTN0bFl58J9CIoH31eKVDJEYCwJRC4nX9jcARV0/0Q5/DvcVUvf8vN2ds
K1OFOTetVZC8o6WBYxKYJRLsMosVG3h5NuA4E06grYoyjQ6J644emEWuLCNQVzLg
peNb1iqweb/4vZ9oGms6WqS14IPfqpRRs+t1DikCgYEAyFtQKlLmNm/EirOMqjS2
4/zMRj4B6n8BB8ZJsVC/MkuD+4B03rY44pP324cJnqVIcug9Zoijffnh1qbtZEc/
MhNCa4a2MY0qcd4r/43cGbjqv94En7e5TgkH/79W+lbR3uBayt33RuuKStauZkf/
2eBi4LlGO5stoeD63h9Ten0=
-----END PRIVATE KEY-----
================================================
FILE: demo11/config/config.go
================================================
package config
import (
"strings"
"github.com/fsnotify/fsnotify"
"github.com/lexkong/log"
"github.com/spf13/viper"
)
type Config struct {
Name string
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置文件
if err := c.initConfig(); err != nil {
return err
}
// 初始化日志包
c.initLog()
// 监控配置文件变化并热加载程序
c.watchConfig()
return nil
}
func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,则解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果没有指定配置文件,则解析默认的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 设置配置文件格式为YAML
viper.AutomaticEnv() // 读取匹配的环境变量
viper.SetEnvPrefix("APISERVER") // 读取环境变量的前缀为APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
}
return nil
}
func (c *Config) initLog() {
passLagerCfg := log.PassLagerCfg{
Writers: viper.GetString("log.writers"),
LoggerLevel: viper.GetString("log.logger_level"),
LoggerFile: viper.GetString("log.logger_file"),
LogFormatText: viper.GetBool("log.log_format_text"
gitextract_0wqow4l0/
├── Makefile
├── README.md
├── a.go
├── demo01/
│ ├── README.md
│ ├── handler/
│ │ └── sd/
│ │ └── check.go
│ ├── main.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo02/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ └── sd/
│ │ └── check.go
│ ├── main.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo03/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ └── sd/
│ │ └── check.go
│ ├── main.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo04/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ └── sd/
│ │ └── check.go
│ ├── main.go
│ ├── model/
│ │ └── init.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo05/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ └── create.go
│ ├── main.go
│ ├── model/
│ │ └── init.go
│ ├── pkg/
│ │ └── errno/
│ │ ├── code.go
│ │ └── errno.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo06/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ └── init.go
│ ├── pkg/
│ │ └── errno/
│ │ ├── code.go
│ │ └── errno.go
│ └── router/
│ ├── middleware/
│ │ └── header.go
│ └── router.go
├── demo07/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ └── errno/
│ │ ├── code.go
│ │ └── errno.go
│ ├── router/
│ │ ├── middleware/
│ │ │ └── header.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo08/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ └── errno/
│ │ ├── code.go
│ │ └── errno.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo09/
│ ├── README.md
│ ├── conf/
│ │ └── config.yaml
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ └── token/
│ │ └── token.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo10/
│ ├── README.md
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ └── token/
│ │ └── token.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo11/
│ ├── Makefile
│ ├── README.md
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ └── token/
│ │ └── token.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo12/
│ ├── Makefile
│ ├── README.md
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo13/
│ ├── Makefile
│ ├── README.md
│ ├── admin.sh
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo14/
│ ├── Makefile
│ ├── README.md
│ ├── admin.sh
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ └── util.go
├── demo15/
│ ├── Makefile
│ ├── README.md
│ ├── admin.sh
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ ├── util.go
│ └── util_test.go
├── demo16/
│ ├── Makefile
│ ├── README.md
│ ├── admin.sh
│ ├── conf/
│ │ ├── config.yaml
│ │ ├── server.crt
│ │ └── server.key
│ ├── config/
│ │ └── config.go
│ ├── db.sql
│ ├── handler/
│ │ ├── handler.go
│ │ ├── sd/
│ │ │ └── check.go
│ │ └── user/
│ │ ├── create.go
│ │ ├── delete.go
│ │ ├── get.go
│ │ ├── list.go
│ │ ├── login.go
│ │ ├── update.go
│ │ └── user.go
│ ├── main.go
│ ├── model/
│ │ ├── init.go
│ │ ├── model.go
│ │ └── user.go
│ ├── pkg/
│ │ ├── auth/
│ │ │ └── auth.go
│ │ ├── constvar/
│ │ │ └── constvar.go
│ │ ├── errno/
│ │ │ ├── code.go
│ │ │ └── errno.go
│ │ ├── token/
│ │ │ └── token.go
│ │ └── version/
│ │ ├── base.go
│ │ ├── doc.go
│ │ └── version.go
│ ├── router/
│ │ ├── middleware/
│ │ │ ├── auth.go
│ │ │ ├── header.go
│ │ │ ├── logging.go
│ │ │ └── requestid.go
│ │ └── router.go
│ ├── service/
│ │ └── service.go
│ └── util/
│ ├── util.go
│ └── util_test.go
└── demo17/
├── Makefile
├── README.md
├── admin.sh
├── conf/
│ ├── config.yaml
│ ├── server.crt
│ └── server.key
├── config/
│ └── config.go
├── db.sql
├── docs/
│ ├── docs.go
│ └── swagger/
│ ├── swagger.json
│ └── swagger.yaml
├── handler/
│ ├── handler.go
│ ├── sd/
│ │ └── check.go
│ └── user/
│ ├── create.go
│ ├── delete.go
│ ├── get.go
│ ├── list.go
│ ├── login.go
│ ├── update.go
│ └── user.go
├── main.go
├── model/
│ ├── init.go
│ ├── model.go
│ └── user.go
├── pkg/
│ ├── auth/
│ │ └── auth.go
│ ├── constvar/
│ │ └── constvar.go
│ ├── errno/
│ │ ├── code.go
│ │ └── errno.go
│ ├── token/
│ │ └── token.go
│ └── version/
│ ├── base.go
│ ├── doc.go
│ └── version.go
├── router/
│ ├── middleware/
│ │ ├── auth.go
│ │ ├── header.go
│ │ ├── logging.go
│ │ └── requestid.go
│ └── router.go
├── service/
│ └── service.go
└── util/
├── util.go
└── util_test.go
SYMBOL INDEX (1052 symbols across 326 files)
FILE: demo01/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo01/main.go
function main (line 14) | func main() {
function pingServer (line 42) | func pingServer() error {
FILE: demo01/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo01/router/router.go
function Load (line 13) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo02/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 31) | func (c *Config) initConfig() error {
method watchConfig (line 51) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo02/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo02/main.go
function main (line 21) | func main() {
function pingServer (line 59) | func pingServer() error {
FILE: demo02/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo02/router/router.go
function Load (line 13) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo03/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo03/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo03/main.go
function main (line 21) | func main() {
function pingServer (line 59) | func pingServer() error {
FILE: demo03/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo03/router/router.go
function Load (line 13) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo04/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo04/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo04/main.go
function main (line 22) | func main() {
function pingServer (line 64) | func pingServer() error {
FILE: demo04/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo04/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo04/router/router.go
function Load (line 13) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo05/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo05/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo05/handler/user/create.go
function Create (line 14) | func Create(c *gin.Context) {
FILE: demo05/main.go
function main (line 22) | func main() {
function pingServer (line 64) | func pingServer() error {
FILE: demo05/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo05/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 31) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 37) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 41) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 46) | func DecodeErr(err error) (int, string) {
FILE: demo05/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo05/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo06/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo06/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo06/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo06/handler/user/create.go
function Create (line 14) | func Create(c *gin.Context) {
FILE: demo06/handler/user/user.go
type CreateRequest (line 3) | type CreateRequest struct
type CreateResponse (line 8) | type CreateResponse struct
FILE: demo06/main.go
function main (line 22) | func main() {
function pingServer (line 64) | func pingServer() error {
FILE: demo06/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo06/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 31) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 37) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 41) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 46) | func DecodeErr(err error) (int, string) {
FILE: demo06/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo06/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo07/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo07/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo07/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo07/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo07/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo07/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo07/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo07/handler/user/list.go
function List (line 12) | func List(c *gin.Context) {
FILE: demo07/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo07/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo07/main.go
function main (line 22) | func main() {
function pingServer (line 64) | func pingServer() error {
FILE: demo07/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo07/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo07/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo07/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo07/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo07/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo07/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo07/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo07/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo07/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo08/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo08/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo08/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo08/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo08/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo08/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo08/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo08/handler/user/list.go
function List (line 12) | func List(c *gin.Context) {
FILE: demo08/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo08/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo08/main.go
function main (line 23) | func main() {
function pingServer (line 64) | func pingServer() error {
FILE: demo08/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo08/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo08/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo08/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo08/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo08/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo08/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo08/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo08/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo08/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo08/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo08/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo09/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo09/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo09/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo09/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo09/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo09/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo09/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo09/handler/user/list.go
function List (line 12) | func List(c *gin.Context) {
FILE: demo09/handler/user/login.go
function Login (line 15) | func Login(c *gin.Context) {
FILE: demo09/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo09/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo09/main.go
function main (line 23) | func main() {
function pingServer (line 64) | func pingServer() error {
FILE: demo09/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo09/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo09/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo09/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo09/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo09/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo09/pkg/token/token.go
type Context (line 19) | type Context struct
function secretFunc (line 25) | func secretFunc(secret string) jwt.Keyfunc {
function Parse (line 38) | func Parse(tokenString string, secret string) (*Context, error) {
function ParseRequest (line 62) | func ParseRequest(c *gin.Context) (*Context, error) {
function Sign (line 79) | func Sign(ctx *gin.Context, c Context, secret string) (tokenString strin...
FILE: demo09/router/middleware/auth.go
function AuthMiddleware (line 11) | func AuthMiddleware() gin.HandlerFunc {
FILE: demo09/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo09/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo09/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo09/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo09/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo09/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo10/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo10/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo10/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo10/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo10/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo10/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo10/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo10/handler/user/list.go
function List (line 12) | func List(c *gin.Context) {
FILE: demo10/handler/user/login.go
function Login (line 15) | func Login(c *gin.Context) {
FILE: demo10/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo10/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo10/main.go
function main (line 23) | func main() {
function pingServer (line 74) | func pingServer() error {
FILE: demo10/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo10/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo10/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo10/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo10/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo10/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo10/pkg/token/token.go
type Context (line 19) | type Context struct
function secretFunc (line 25) | func secretFunc(secret string) jwt.Keyfunc {
function Parse (line 38) | func Parse(tokenString string, secret string) (*Context, error) {
function ParseRequest (line 62) | func ParseRequest(c *gin.Context) (*Context, error) {
function Sign (line 79) | func Sign(ctx *gin.Context, c Context, secret string) (tokenString strin...
FILE: demo10/router/middleware/auth.go
function AuthMiddleware (line 11) | func AuthMiddleware() gin.HandlerFunc {
FILE: demo10/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo10/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo10/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo10/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo10/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo10/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo11/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo11/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo11/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo11/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo11/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo11/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo11/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo11/handler/user/list.go
function List (line 12) | func List(c *gin.Context) {
FILE: demo11/handler/user/login.go
function Login (line 15) | func Login(c *gin.Context) {
FILE: demo11/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo11/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo11/main.go
function main (line 23) | func main() {
function pingServer (line 74) | func pingServer() error {
FILE: demo11/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo11/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo11/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo11/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo11/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo11/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo11/pkg/token/token.go
type Context (line 19) | type Context struct
function secretFunc (line 25) | func secretFunc(secret string) jwt.Keyfunc {
function Parse (line 38) | func Parse(tokenString string, secret string) (*Context, error) {
function ParseRequest (line 62) | func ParseRequest(c *gin.Context) (*Context, error) {
function Sign (line 79) | func Sign(ctx *gin.Context, c Context, secret string) (tokenString strin...
FILE: demo11/router/middleware/auth.go
function AuthMiddleware (line 11) | func AuthMiddleware() gin.HandlerFunc {
FILE: demo11/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo11/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo11/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo11/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo11/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo11/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo12/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo12/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo12/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo12/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo12/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo12/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo12/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo12/handler/user/list.go
function List (line 12) | func List(c *gin.Context) {
FILE: demo12/handler/user/login.go
function Login (line 15) | func Login(c *gin.Context) {
FILE: demo12/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo12/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo12/main.go
function main (line 28) | func main() {
function pingServer (line 90) | func pingServer() error {
FILE: demo12/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo12/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo12/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo12/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo12/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo12/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo12/pkg/token/token.go
type Context (line 19) | type Context struct
function secretFunc (line 25) | func secretFunc(secret string) jwt.Keyfunc {
function Parse (line 38) | func Parse(tokenString string, secret string) (*Context, error) {
function ParseRequest (line 62) | func ParseRequest(c *gin.Context) (*Context, error) {
function Sign (line 79) | func Sign(ctx *gin.Context, c Context, secret string) (tokenString strin...
FILE: demo12/pkg/version/version.go
type Info (line 9) | type Info struct
method String (line 20) | func (info Info) String() string {
function Get (line 24) | func Get() Info {
FILE: demo12/router/middleware/auth.go
function AuthMiddleware (line 11) | func AuthMiddleware() gin.HandlerFunc {
FILE: demo12/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo12/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo12/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo12/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo12/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo12/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo13/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo13/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo13/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo13/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo13/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo13/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo13/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo13/handler/user/list.go
function List (line 12) | func List(c *gin.Context) {
FILE: demo13/handler/user/login.go
function Login (line 15) | func Login(c *gin.Context) {
FILE: demo13/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo13/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo13/main.go
function main (line 28) | func main() {
function pingServer (line 90) | func pingServer() error {
FILE: demo13/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo13/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo13/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo13/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo13/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo13/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo13/pkg/token/token.go
type Context (line 19) | type Context struct
function secretFunc (line 25) | func secretFunc(secret string) jwt.Keyfunc {
function Parse (line 38) | func Parse(tokenString string, secret string) (*Context, error) {
function ParseRequest (line 62) | func ParseRequest(c *gin.Context) (*Context, error) {
function Sign (line 79) | func Sign(ctx *gin.Context, c Context, secret string) (tokenString strin...
FILE: demo13/pkg/version/version.go
type Info (line 9) | type Info struct
method String (line 20) | func (info Info) String() string {
function Get (line 24) | func Get() Info {
FILE: demo13/router/middleware/auth.go
function AuthMiddleware (line 11) | func AuthMiddleware() gin.HandlerFunc {
FILE: demo13/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo13/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo13/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo13/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo13/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo13/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo14/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo14/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo14/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo14/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo14/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo14/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo14/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo14/handler/user/list.go
function List (line 13) | func List(c *gin.Context) {
FILE: demo14/handler/user/login.go
function Login (line 15) | func Login(c *gin.Context) {
FILE: demo14/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo14/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo14/main.go
function main (line 28) | func main() {
function pingServer (line 90) | func pingServer() error {
FILE: demo14/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo14/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo14/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo14/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo14/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo14/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo14/pkg/token/token.go
type Context (line 19) | type Context struct
function secretFunc (line 25) | func secretFunc(secret string) jwt.Keyfunc {
function Parse (line 38) | func Parse(tokenString string, secret string) (*Context, error) {
function ParseRequest (line 62) | func ParseRequest(c *gin.Context) (*Context, error) {
function Sign (line 79) | func Sign(ctx *gin.Context, c Context, secret string) (tokenString strin...
FILE: demo14/pkg/version/version.go
type Info (line 9) | type Info struct
method String (line 20) | func (info Info) String() string {
function Get (line 24) | func Get() Info {
FILE: demo14/router/middleware/auth.go
function AuthMiddleware (line 11) | func AuthMiddleware() gin.HandlerFunc {
FILE: demo14/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo14/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo14/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo14/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo14/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo14/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo15/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo15/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo15/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo15/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo15/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo15/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo15/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo15/handler/user/list.go
function List (line 13) | func List(c *gin.Context) {
FILE: demo15/handler/user/login.go
function Login (line 15) | func Login(c *gin.Context) {
FILE: demo15/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo15/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo15/main.go
function main (line 28) | func main() {
function pingServer (line 90) | func pingServer() error {
FILE: demo15/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo15/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo15/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo15/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo15/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo15/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo15/pkg/token/token.go
type Context (line 19) | type Context struct
function secretFunc (line 25) | func secretFunc(secret string) jwt.Keyfunc {
function Parse (line 38) | func Parse(tokenString string, secret string) (*Context, error) {
function ParseRequest (line 62) | func ParseRequest(c *gin.Context) (*Context, error) {
function Sign (line 79) | func Sign(ctx *gin.Context, c Context, secret string) (tokenString strin...
FILE: demo15/pkg/version/version.go
type Info (line 9) | type Info struct
method String (line 20) | func (info Info) String() string {
function Get (line 24) | func Get() Info {
FILE: demo15/router/middleware/auth.go
function AuthMiddleware (line 11) | func AuthMiddleware() gin.HandlerFunc {
FILE: demo15/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo15/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo15/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo15/router/router.go
function Load (line 14) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo15/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo15/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo15/util/util_test.go
function TestGenShortId (line 7) | func TestGenShortId(t *testing.T) {
function BenchmarkGenShortId (line 16) | func BenchmarkGenShortId(b *testing.B) {
function BenchmarkGenShortIdTimeConsuming (line 22) | func BenchmarkGenShortIdTimeConsuming(b *testing.B) {
FILE: demo16/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo16/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo16/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo16/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 22) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 28) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 53) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 77) | func RAMCheck(c *gin.Context) {
FILE: demo16/handler/user/create.go
function Create (line 15) | func Create(c *gin.Context) {
FILE: demo16/handler/user/delete.go
function Delete (line 14) | func Delete(c *gin.Context) {
FILE: demo16/handler/user/get.go
function Get (line 12) | func Get(c *gin.Context) {
FILE: demo16/handler/user/list.go
function List (line 13) | func List(c *gin.Context) {
FILE: demo16/handler/user/login.go
function Login (line 15) | func Login(c *gin.Context) {
FILE: demo16/handler/user/update.go
function Update (line 17) | func Update(c *gin.Context) {
FILE: demo16/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
FILE: demo16/main.go
function main (line 29) | func main() {
function pingServer (line 91) | func pingServer() error {
FILE: demo16/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo16/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo16/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo16/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo16/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo16/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo16/pkg/token/token.go
type Context (line 19) | type Context struct
function secretFunc (line 25) | func secretFunc(secret string) jwt.Keyfunc {
function Parse (line 38) | func Parse(tokenString string, secret string) (*Context, error) {
function ParseRequest (line 62) | func ParseRequest(c *gin.Context) (*Context, error) {
function Sign (line 79) | func Sign(ctx *gin.Context, c Context, secret string) (tokenString strin...
FILE: demo16/pkg/version/version.go
type Info (line 9) | type Info struct
method String (line 20) | func (info Info) String() string {
function Get (line 24) | func Get() Info {
FILE: demo16/router/middleware/auth.go
function AuthMiddleware (line 11) | func AuthMiddleware() gin.HandlerFunc {
FILE: demo16/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo16/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo16/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo16/router/router.go
function Load (line 15) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo16/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo16/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo16/util/util_test.go
function TestGenShortId (line 7) | func TestGenShortId(t *testing.T) {
function BenchmarkGenShortId (line 16) | func BenchmarkGenShortId(b *testing.B) {
function BenchmarkGenShortIdTimeConsuming (line 22) | func BenchmarkGenShortIdTimeConsuming(b *testing.B) {
FILE: demo17/config/config.go
type Config (line 11) | type Config struct
method initConfig (line 34) | func (c *Config) initConfig() error {
method initLog (line 53) | func (c *Config) initLog() {
method watchConfig (line 69) | func (c *Config) watchConfig() {
function Init (line 15) | func Init(cfg string) error {
FILE: demo17/db.sql
type `tb_users` (line 23) | CREATE TABLE `tb_users` (
FILE: demo17/docs/docs.go
type s (line 413) | type s struct
method ReadDoc (line 415) | func (s *s) ReadDoc() string {
function init (line 418) | func init() {
FILE: demo17/handler/handler.go
type Response (line 11) | type Response struct
function SendResponse (line 17) | func SendResponse(c *gin.Context, err error, data interface{}) {
FILE: demo17/handler/sd/check.go
constant B (line 15) | B = 1
constant KB (line 16) | KB = 1024 * B
constant MB (line 17) | MB = 1024 * KB
constant GB (line 18) | GB = 1024 * MB
function HealthCheck (line 28) | func HealthCheck(c *gin.Context) {
function DiskCheck (line 40) | func DiskCheck(c *gin.Context) {
function CPUCheck (line 71) | func CPUCheck(c *gin.Context) {
function RAMCheck (line 101) | func RAMCheck(c *gin.Context) {
FILE: demo17/handler/user/create.go
function Create (line 22) | func Create(c *gin.Context) {
FILE: demo17/handler/user/delete.go
function Delete (line 21) | func Delete(c *gin.Context) {
FILE: demo17/handler/user/get.go
function Get (line 19) | func Get(c *gin.Context) {
FILE: demo17/handler/user/list.go
function List (line 20) | func List(c *gin.Context) {
FILE: demo17/handler/user/login.go
function Login (line 19) | func Login(c *gin.Context) {
FILE: demo17/handler/user/update.go
function Update (line 25) | func Update(c *gin.Context) {
FILE: demo17/handler/user/user.go
type CreateRequest (line 7) | type CreateRequest struct
type CreateResponse (line 12) | type CreateResponse struct
type ListRequest (line 16) | type ListRequest struct
type ListResponse (line 22) | type ListResponse struct
type SwaggerListResponse (line 27) | type SwaggerListResponse struct
FILE: demo17/main.go
function main (line 39) | func main() {
function pingServer (line 101) | func pingServer() error {
FILE: demo17/model/init.go
type Database (line 13) | type Database struct
method Init (line 70) | func (db *Database) Init() {
method Close (line 77) | func (db *Database) Close() {
function openDB (line 20) | func openDB(username, password, addr, name string) *gorm.DB {
function setupDB (line 41) | func setupDB(db *gorm.DB) {
function InitSelfDB (line 48) | func InitSelfDB() *gorm.DB {
function GetSelfDB (line 55) | func GetSelfDB() *gorm.DB {
function InitDockerDB (line 59) | func InitDockerDB() *gorm.DB {
function GetDockerDB (line 66) | func GetDockerDB() *gorm.DB {
FILE: demo17/model/model.go
type BaseModel (line 8) | type BaseModel struct
type UserInfo (line 15) | type UserInfo struct
type UserList (line 24) | type UserList struct
type Token (line 30) | type Token struct
FILE: demo17/model/user.go
type UserModel (line 13) | type UserModel struct
method TableName (line 19) | func (c *UserModel) TableName() string {
method Create (line 24) | func (u *UserModel) Create() error {
method Update (line 36) | func (u *UserModel) Update() error {
method Compare (line 69) | func (u *UserModel) Compare(pwd string) (err error) {
method Encrypt (line 75) | func (u *UserModel) Encrypt() (err error) {
method Validate (line 81) | func (u *UserModel) Validate() error {
function DeleteUser (line 29) | func DeleteUser(id uint64) error {
function GetUser (line 41) | func GetUser(username string) (*UserModel, error) {
function ListUser (line 48) | func ListUser(username string, offset, limit int) ([]*UserModel, uint64,...
FILE: demo17/pkg/auth/auth.go
function Encrypt (line 6) | func Encrypt(source string) (string, error) {
function Compare (line 12) | func Compare(hashedPassword, password string) error {
FILE: demo17/pkg/constvar/constvar.go
constant DefaultLimit (line 4) | DefaultLimit = 50
FILE: demo17/pkg/errno/errno.go
type Errno (line 5) | type Errno struct
method Error (line 10) | func (err Errno) Error() string {
type Err (line 15) | type Err struct
method Add (line 25) | func (err *Err) Add(message string) error {
method Addf (line 30) | func (err *Err) Addf(format string, args ...interface{}) error {
method Error (line 35) | func (err *Err) Error() string {
function New (line 21) | func New(errno *Errno, err error) *Err {
function IsErrUserNotFound (line 39) | func IsErrUserNotFound(err error) bool {
function DecodeErr (line 44) | func DecodeErr(err error) (int, string) {
FILE: demo17/pkg/token/token.go
type Context (line 19) | type Context struct
function secretFunc (line 25) | func secretFunc(secret string) jwt.Keyfunc {
function Parse (line 38) | func Parse(tokenString string, secret string) (*Context, error) {
function ParseRequest (line 62) | func ParseRequest(c *gin.Context) (*Context, error) {
function Sign (line 79) | func Sign(ctx *gin.Context, c Context, secret string) (tokenString strin...
FILE: demo17/pkg/version/version.go
type Info (line 9) | type Info struct
method String (line 20) | func (info Info) String() string {
function Get (line 24) | func Get() Info {
FILE: demo17/router/middleware/auth.go
function AuthMiddleware (line 11) | func AuthMiddleware() gin.HandlerFunc {
FILE: demo17/router/middleware/header.go
function NoCache (line 12) | func NoCache(c *gin.Context) {
function Options (line 22) | func Options(c *gin.Context) {
function Secure (line 37) | func Secure(c *gin.Context) {
FILE: demo17/router/middleware/logging.go
type bodyLogWriter (line 18) | type bodyLogWriter struct
method Write (line 23) | func (w bodyLogWriter) Write(b []byte) (int, error) {
function Logging (line 29) | func Logging() gin.HandlerFunc {
FILE: demo17/router/middleware/requestid.go
function RequestId (line 8) | func RequestId() gin.HandlerFunc {
FILE: demo17/router/router.go
function Load (line 18) | func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
FILE: demo17/service/service.go
function ListUser (line 11) | func ListUser(username string, offset, limit int) ([]*model.UserInfo, ui...
FILE: demo17/util/util.go
function GenShortId (line 8) | func GenShortId() (string, error) {
function GetReqID (line 12) | func GetReqID(c *gin.Context) string {
FILE: demo17/util/util_test.go
function TestGenShortId (line 7) | func TestGenShortId(t *testing.T) {
function BenchmarkGenShortId (line 16) | func BenchmarkGenShortId(b *testing.B) {
function BenchmarkGenShortIdTimeConsuming (line 22) | func BenchmarkGenShortIdTimeConsuming(b *testing.B) {
Condensed preview — 417 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (545K chars).
[
{
"path": "Makefile",
"chars": 76,
"preview": "clean:\n\tfind . -name \"[._]*.s[a-w][a-z]\" | xargs -i rm -f {} \n.PHONY: clean\n"
},
{
"path": "README.md",
"chars": 1239,
"preview": "## 目录\n\n**注意:** 此项目不再维护,如果想学习本项目可以移步:https://github.com/marmotedu/goserver\n\n**另外**:此课程已经升级为极客时间课程,课程介绍[《Go 语言项目开发实战》](htt"
},
{
"path": "a.go",
"chars": 1,
"preview": "\n"
},
{
"path": "demo01/README.md",
"chars": 26,
"preview": "实战:启动一个最简单的RESTful API服务器\n"
},
{
"path": "demo01/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo01/main.go",
"chars": 1199,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/router\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc ma"
},
{
"path": "demo01/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo01/router/router.go",
"chars": 695,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/router/middleware\"\n\n\t\"github.com/gin-gonic/gin"
},
{
"path": "demo02/README.md",
"chars": 10,
"preview": "实战:配置文件读取\n"
},
{
"path": "demo02/conf/config.yaml",
"chars": 256,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo02/config/config.go",
"chars": 1031,
"preview": "package config\n\nimport (\n\t\"log\"\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n)\n\ntype Config str"
},
{
"path": "demo02/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo02/main.go",
"chars": 1563,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/router\"\n\n\t\"github.com/gin-g"
},
{
"path": "demo02/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo02/router/router.go",
"chars": 695,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/router/middleware\"\n\n\t\"github.com/gin-gonic/gin"
},
{
"path": "demo03/README.md",
"chars": 14,
"preview": "实战:记录和管理API日志\n"
},
{
"path": "demo03/conf/config.yaml",
"chars": 450,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo03/config/config.go",
"chars": 1617,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\""
},
{
"path": "demo03/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo03/main.go",
"chars": 1577,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/router\"\n\n\t\"github.com/gin-gonic/gi"
},
{
"path": "demo03/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo03/router/router.go",
"chars": 695,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/router/middleware\"\n\n\t\"github.com/gin-gonic/gin"
},
{
"path": "demo04/README.md",
"chars": 20,
"preview": "实战:初始化Mysql数据库并建立连接\n"
},
{
"path": "demo04/conf/config.yaml",
"chars": 450,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo04/config/config.go",
"chars": 1617,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\""
},
{
"path": "demo04/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo04/main.go",
"chars": 1650,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/model\"\n\t\"apiserver/router\"\n\n\t\"gith"
},
{
"path": "demo04/model/init.go",
"chars": 1589,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\"\n\t// MySQL driver.\n\t\"github.com/jinzh"
},
{
"path": "demo04/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo04/router/router.go",
"chars": 695,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/router/middleware\"\n\n\t\"github.com/gin-gonic/gin"
},
{
"path": "demo05/README.md",
"chars": 13,
"preview": "实战:自定义业务错误信息\n"
},
{
"path": "demo05/conf/config.yaml",
"chars": 450,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo05/config/config.go",
"chars": 1617,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\""
},
{
"path": "demo05/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo05/handler/user/create.go",
"chars": 919,
"preview": "package user\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/lexkong/log\""
},
{
"path": "demo05/main.go",
"chars": 1650,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/model\"\n\t\"apiserver/router\"\n\n\t\"gith"
},
{
"path": "demo05/model/init.go",
"chars": 1589,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\"\n\t// MySQL driver.\n\t\"github.com/jinzh"
},
{
"path": "demo05/pkg/errno/code.go",
"chars": 381,
"preview": "package errno\n\nvar (\n\t// Common errors\n\tOK = &Errno{Code: 0, Message: \"OK\"}\n\tInternalServerError = &Err"
},
{
"path": "demo05/pkg/errno/errno.go",
"chars": 1225,
"preview": "package errno\n\nimport \"fmt\"\n\ntype Errno struct {\n\tCode int\n\tMessage string\n}\n\nfunc (err Errno) Error() string {\n\tretu"
},
{
"path": "demo05/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo05/router/router.go",
"chars": 780,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/handler/user\"\n\t\"apiserver/router/middleware\"\n\n"
},
{
"path": "demo06/README.md",
"chars": 15,
"preview": "实战:读取和返回HTTP请求\n"
},
{
"path": "demo06/conf/config.yaml",
"chars": 450,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo06/config/config.go",
"chars": 1617,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\""
},
{
"path": "demo06/handler/handler.go",
"chars": 458,
"preview": "package handler\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype Response struct {\n\tCo"
},
{
"path": "demo06/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo06/handler/user/create.go",
"chars": 967,
"preview": "package user\n\nimport (\n\t\"fmt\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/l"
},
{
"path": "demo06/handler/user/user.go",
"chars": 181,
"preview": "package user\n\ntype CreateRequest struct {\n\tUsername string `json:\"username\"`\n\tPassword string `json:\"password\"`\n}\n\ntype "
},
{
"path": "demo06/main.go",
"chars": 1650,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/model\"\n\t\"apiserver/router\"\n\n\t\"gith"
},
{
"path": "demo06/model/init.go",
"chars": 1589,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\"\n\t// MySQL driver.\n\t\"github.com/jinzh"
},
{
"path": "demo06/pkg/errno/code.go",
"chars": 381,
"preview": "package errno\n\nvar (\n\t// Common errors\n\tOK = &Errno{Code: 0, Message: \"OK\"}\n\tInternalServerError = &Err"
},
{
"path": "demo06/pkg/errno/errno.go",
"chars": 1225,
"preview": "package errno\n\nimport \"fmt\"\n\ntype Errno struct {\n\tCode int\n\tMessage string\n}\n\nfunc (err Errno) Error() string {\n\tretu"
},
{
"path": "demo06/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo06/router/router.go",
"chars": 790,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/handler/user\"\n\t\"apiserver/router/middleware\"\n\n"
},
{
"path": "demo07/README.md",
"chars": 18,
"preview": "实战:用户业务逻辑处理(业务处理)\n"
},
{
"path": "demo07/conf/config.yaml",
"chars": 621,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo07/config/config.go",
"chars": 1617,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\""
},
{
"path": "demo07/db.sql",
"chars": 2210,
"preview": "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SE"
},
{
"path": "demo07/handler/handler.go",
"chars": 458,
"preview": "package handler\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype Response struct {\n\tCo"
},
{
"path": "demo07/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo07/handler/user/create.go",
"chars": 1014,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t\"github.com/"
},
{
"path": "demo07/handler/user/delete.go",
"chars": 395,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-go"
},
{
"path": "demo07/handler/user/get.go",
"chars": 422,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n"
},
{
"path": "demo07/handler/user/list.go",
"chars": 508,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/service\"\n\n\t\"github.com/gin-gonic/gin\"\n)"
},
{
"path": "demo07/handler/user/update.go",
"chars": 1045,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t"
},
{
"path": "demo07/handler/user/user.go",
"chars": 469,
"preview": "package user\n\nimport (\n\t\"apiserver/model\"\n)\n\ntype CreateRequest struct {\n\tUsername string `json:\"username\"`\n\tPassword st"
},
{
"path": "demo07/main.go",
"chars": 1650,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/model\"\n\t\"apiserver/router\"\n\n\t\"gith"
},
{
"path": "demo07/model/init.go",
"chars": 1589,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\"\n\t// MySQL driver.\n\t\"github.com/jinzh"
},
{
"path": "demo07/model/model.go",
"chars": 730,
"preview": "package model\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype BaseModel struct {\n\tId uint64 `gorm:\"primary_key;AUTO_INCREME"
},
{
"path": "demo07/model/user.go",
"chars": 2118,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"apiserver/pkg/auth\"\n\t\"apiserver/pkg/constvar\"\n\n\tvalidator \"gopkg.in/go-playground/vali"
},
{
"path": "demo07/pkg/auth/auth.go",
"chars": 479,
"preview": "package auth\n\nimport \"golang.org/x/crypto/bcrypt\"\n\n// Encrypt encrypts the plain text with bcrypt.\nfunc Encrypt(source s"
},
{
"path": "demo07/pkg/constvar/constvar.go",
"chars": 47,
"preview": "package constvar\n\nconst (\n\tDefaultLimit = 50\n)\n"
},
{
"path": "demo07/pkg/errno/code.go",
"chars": 887,
"preview": "package errno\n\nvar (\n\t// Common errors\n\tOK = &Errno{Code: 0, Message: \"OK\"}\n\tInternalServerError = &Err"
},
{
"path": "demo07/pkg/errno/errno.go",
"chars": 1077,
"preview": "package errno\n\nimport \"fmt\"\n\ntype Errno struct {\n\tCode int\n\tMessage string\n}\n\nfunc (err Errno) Error() string {\n\tretu"
},
{
"path": "demo07/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo07/router/router.go",
"chars": 896,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/handler/user\"\n\t\"apiserver/router/middleware\"\n\n"
},
{
"path": "demo07/service/service.go",
"chars": 1410,
"preview": "package service\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"apiserver/model\"\n\t\"apiserver/util\"\n)\n\nfunc ListUser(username string, offset,"
},
{
"path": "demo07/util/util.go",
"chars": 320,
"preview": "package util\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/teris-io/shortid\"\n)\n\nfunc GenShortId() (string, error) {"
},
{
"path": "demo08/README.md",
"chars": 19,
"preview": "实战:HTTP调用添加自定义处理逻辑\n"
},
{
"path": "demo08/conf/config.yaml",
"chars": 621,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo08/config/config.go",
"chars": 1617,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\""
},
{
"path": "demo08/db.sql",
"chars": 2210,
"preview": "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SE"
},
{
"path": "demo08/handler/handler.go",
"chars": 458,
"preview": "package handler\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype Response struct {\n\tCo"
},
{
"path": "demo08/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo08/handler/user/create.go",
"chars": 1014,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t\"github.com/"
},
{
"path": "demo08/handler/user/delete.go",
"chars": 395,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-go"
},
{
"path": "demo08/handler/user/get.go",
"chars": 422,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n"
},
{
"path": "demo08/handler/user/list.go",
"chars": 508,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/service\"\n\n\t\"github.com/gin-gonic/gin\"\n)"
},
{
"path": "demo08/handler/user/update.go",
"chars": 1045,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t"
},
{
"path": "demo08/handler/user/user.go",
"chars": 469,
"preview": "package user\n\nimport (\n\t\"apiserver/model\"\n)\n\ntype CreateRequest struct {\n\tUsername string `json:\"username\"`\n\tPassword st"
},
{
"path": "demo08/main.go",
"chars": 1676,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/model\"\n\t\"apiserver/router\"\n\t\"apise"
},
{
"path": "demo08/model/init.go",
"chars": 1589,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\"\n\t// MySQL driver.\n\t\"github.com/jinzh"
},
{
"path": "demo08/model/model.go",
"chars": 730,
"preview": "package model\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype BaseModel struct {\n\tId uint64 `gorm:\"primary_key;AUTO_INCREME"
},
{
"path": "demo08/model/user.go",
"chars": 2118,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"apiserver/pkg/auth\"\n\t\"apiserver/pkg/constvar\"\n\n\tvalidator \"gopkg.in/go-playground/vali"
},
{
"path": "demo08/pkg/auth/auth.go",
"chars": 479,
"preview": "package auth\n\nimport \"golang.org/x/crypto/bcrypt\"\n\n// Encrypt encrypts the plain text with bcrypt.\nfunc Encrypt(source s"
},
{
"path": "demo08/pkg/constvar/constvar.go",
"chars": 47,
"preview": "package constvar\n\nconst (\n\tDefaultLimit = 50\n)\n"
},
{
"path": "demo08/pkg/errno/code.go",
"chars": 887,
"preview": "package errno\n\nvar (\n\t// Common errors\n\tOK = &Errno{Code: 0, Message: \"OK\"}\n\tInternalServerError = &Err"
},
{
"path": "demo08/pkg/errno/errno.go",
"chars": 1077,
"preview": "package errno\n\nimport \"fmt\"\n\ntype Errno struct {\n\tCode int\n\tMessage string\n}\n\nfunc (err Errno) Error() string {\n\tretu"
},
{
"path": "demo08/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo08/router/middleware/logging.go",
"chars": 2016,
"preview": "package middleware\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"regexp\"\n\t\"time\"\n\n\t\"apiserver/handler\"\n\t\"apiserver/"
},
{
"path": "demo08/router/middleware/requestid.go",
"chars": 545,
"preview": "package middleware\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/satori/go.uuid\"\n)\n\nfunc RequestId() gin.HandlerFun"
},
{
"path": "demo08/router/router.go",
"chars": 896,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/handler/user\"\n\t\"apiserver/router/middleware\"\n\n"
},
{
"path": "demo08/service/service.go",
"chars": 1410,
"preview": "package service\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"apiserver/model\"\n\t\"apiserver/util\"\n)\n\nfunc ListUser(username string, offset,"
},
{
"path": "demo08/util/util.go",
"chars": 320,
"preview": "package util\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/teris-io/shortid\"\n)\n\nfunc GenShortId() (string, error) {"
},
{
"path": "demo09/README.md",
"chars": 11,
"preview": "实战:API身份验证\n"
},
{
"path": "demo09/conf/config.yaml",
"chars": 680,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo09/config/config.go",
"chars": 1617,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\""
},
{
"path": "demo09/db.sql",
"chars": 2210,
"preview": "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SE"
},
{
"path": "demo09/handler/handler.go",
"chars": 458,
"preview": "package handler\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype Response struct {\n\tCo"
},
{
"path": "demo09/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo09/handler/user/create.go",
"chars": 1014,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t\"github.com/"
},
{
"path": "demo09/handler/user/delete.go",
"chars": 395,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-go"
},
{
"path": "demo09/handler/user/get.go",
"chars": 422,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n"
},
{
"path": "demo09/handler/user/list.go",
"chars": 508,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/service\"\n\n\t\"github.com/gin-gonic/gin\"\n)"
},
{
"path": "demo09/handler/user/login.go",
"chars": 1017,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/auth\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserve"
},
{
"path": "demo09/handler/user/update.go",
"chars": 1045,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t"
},
{
"path": "demo09/handler/user/user.go",
"chars": 469,
"preview": "package user\n\nimport (\n\t\"apiserver/model\"\n)\n\ntype CreateRequest struct {\n\tUsername string `json:\"username\"`\n\tPassword st"
},
{
"path": "demo09/main.go",
"chars": 1676,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/model\"\n\t\"apiserver/router\"\n\t\"apise"
},
{
"path": "demo09/model/init.go",
"chars": 1589,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\"\n\t// MySQL driver.\n\t\"github.com/jinzh"
},
{
"path": "demo09/model/model.go",
"chars": 730,
"preview": "package model\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype BaseModel struct {\n\tId uint64 `gorm:\"primary_key;AUTO_INCREME"
},
{
"path": "demo09/model/user.go",
"chars": 2118,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"apiserver/pkg/auth\"\n\t\"apiserver/pkg/constvar\"\n\n\tvalidator \"gopkg.in/go-playground/vali"
},
{
"path": "demo09/pkg/auth/auth.go",
"chars": 479,
"preview": "package auth\n\nimport \"golang.org/x/crypto/bcrypt\"\n\n// Encrypt encrypts the plain text with bcrypt.\nfunc Encrypt(source s"
},
{
"path": "demo09/pkg/constvar/constvar.go",
"chars": 47,
"preview": "package constvar\n\nconst (\n\tDefaultLimit = 50\n)\n"
},
{
"path": "demo09/pkg/errno/code.go",
"chars": 887,
"preview": "package errno\n\nvar (\n\t// Common errors\n\tOK = &Errno{Code: 0, Message: \"OK\"}\n\tInternalServerError = &Err"
},
{
"path": "demo09/pkg/errno/errno.go",
"chars": 1077,
"preview": "package errno\n\nimport \"fmt\"\n\ntype Errno struct {\n\tCode int\n\tMessage string\n}\n\nfunc (err Errno) Error() string {\n\tretu"
},
{
"path": "demo09/pkg/token/token.go",
"chars": 2399,
"preview": "package token\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\tjwt \"github.com/dgrijalva/jwt-go\"\n\t\"github.com/gin-gonic/gin\"\n\t\"githu"
},
{
"path": "demo09/router/middleware/auth.go",
"chars": 380,
"preview": "package middleware\n\nimport (\n\t\"apiserver/handler\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/pkg/token\"\n\n\t\"github.com/gin-gonic/"
},
{
"path": "demo09/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo09/router/middleware/logging.go",
"chars": 2016,
"preview": "package middleware\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"regexp\"\n\t\"time\"\n\n\t\"apiserver/handler\"\n\t\"apiserver/"
},
{
"path": "demo09/router/middleware/requestid.go",
"chars": 545,
"preview": "package middleware\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/satori/go.uuid\"\n)\n\nfunc RequestId() gin.HandlerFun"
},
{
"path": "demo09/router/router.go",
"chars": 1054,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/handler/user\"\n\t\"apiserver/router/middleware\"\n\n"
},
{
"path": "demo09/service/service.go",
"chars": 1410,
"preview": "package service\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"apiserver/model\"\n\t\"apiserver/util\"\n)\n\nfunc ListUser(username string, offset,"
},
{
"path": "demo09/util/util.go",
"chars": 320,
"preview": "package util\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/teris-io/shortid\"\n)\n\nfunc GenShortId() (string, error) {"
},
{
"path": "demo10/README.md",
"chars": 17,
"preview": "进阶:用HTTPS加密API请求\n"
},
{
"path": "demo10/conf/config.yaml",
"chars": 740,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo10/conf/server.crt",
"chars": 1395,
"preview": "-----BEGIN CERTIFICATE-----\nMIID2TCCAsGgAwIBAgIJAMQRszo28YxYMA0GCSqGSIb3DQEBBQUAMIGCMQswCQYD\nVQQGEwJERTEMMAoGA1UECAwDTlJ"
},
{
"path": "demo10/conf/server.key",
"chars": 1704,
"preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8OgsyZRcTYO/a\n0mxWv0J9SE4Qzm/jCjjqUI3lgkq"
},
{
"path": "demo10/config/config.go",
"chars": 1617,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\""
},
{
"path": "demo10/db.sql",
"chars": 2210,
"preview": "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SE"
},
{
"path": "demo10/handler/handler.go",
"chars": 458,
"preview": "package handler\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype Response struct {\n\tCo"
},
{
"path": "demo10/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo10/handler/user/create.go",
"chars": 1014,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t\"github.com/"
},
{
"path": "demo10/handler/user/delete.go",
"chars": 395,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-go"
},
{
"path": "demo10/handler/user/get.go",
"chars": 422,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n"
},
{
"path": "demo10/handler/user/list.go",
"chars": 508,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/service\"\n\n\t\"github.com/gin-gonic/gin\"\n)"
},
{
"path": "demo10/handler/user/login.go",
"chars": 1017,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/auth\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserve"
},
{
"path": "demo10/handler/user/update.go",
"chars": 1045,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t"
},
{
"path": "demo10/handler/user/user.go",
"chars": 469,
"preview": "package user\n\nimport (\n\t\"apiserver/model\"\n)\n\ntype CreateRequest struct {\n\tUsername string `json:\"username\"`\n\tPassword st"
},
{
"path": "demo10/main.go",
"chars": 2042,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/model\"\n\t\"apiserver/router\"\n\t\"apise"
},
{
"path": "demo10/model/init.go",
"chars": 1589,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\"\n\t// MySQL driver.\n\t\"github.com/jinzh"
},
{
"path": "demo10/model/model.go",
"chars": 730,
"preview": "package model\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype BaseModel struct {\n\tId uint64 `gorm:\"primary_key;AUTO_INCREME"
},
{
"path": "demo10/model/user.go",
"chars": 2118,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"apiserver/pkg/auth\"\n\t\"apiserver/pkg/constvar\"\n\n\tvalidator \"gopkg.in/go-playground/vali"
},
{
"path": "demo10/pkg/auth/auth.go",
"chars": 479,
"preview": "package auth\n\nimport \"golang.org/x/crypto/bcrypt\"\n\n// Encrypt encrypts the plain text with bcrypt.\nfunc Encrypt(source s"
},
{
"path": "demo10/pkg/constvar/constvar.go",
"chars": 47,
"preview": "package constvar\n\nconst (\n\tDefaultLimit = 50\n)\n"
},
{
"path": "demo10/pkg/errno/code.go",
"chars": 887,
"preview": "package errno\n\nvar (\n\t// Common errors\n\tOK = &Errno{Code: 0, Message: \"OK\"}\n\tInternalServerError = &Err"
},
{
"path": "demo10/pkg/errno/errno.go",
"chars": 1077,
"preview": "package errno\n\nimport \"fmt\"\n\ntype Errno struct {\n\tCode int\n\tMessage string\n}\n\nfunc (err Errno) Error() string {\n\tretu"
},
{
"path": "demo10/pkg/token/token.go",
"chars": 2399,
"preview": "package token\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\tjwt \"github.com/dgrijalva/jwt-go\"\n\t\"github.com/gin-gonic/gin\"\n\t\"githu"
},
{
"path": "demo10/router/middleware/auth.go",
"chars": 380,
"preview": "package middleware\n\nimport (\n\t\"apiserver/handler\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/pkg/token\"\n\n\t\"github.com/gin-gonic/"
},
{
"path": "demo10/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo10/router/middleware/logging.go",
"chars": 2016,
"preview": "package middleware\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"regexp\"\n\t\"time\"\n\n\t\"apiserver/handler\"\n\t\"apiserver/"
},
{
"path": "demo10/router/middleware/requestid.go",
"chars": 545,
"preview": "package middleware\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/satori/go.uuid\"\n)\n\nfunc RequestId() gin.HandlerFun"
},
{
"path": "demo10/router/router.go",
"chars": 1054,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/handler/user\"\n\t\"apiserver/router/middleware\"\n\n"
},
{
"path": "demo10/service/service.go",
"chars": 1410,
"preview": "package service\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"apiserver/model\"\n\t\"apiserver/util\"\n)\n\nfunc ListUser(username string, offset,"
},
{
"path": "demo10/util/util.go",
"chars": 320,
"preview": "package util\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/teris-io/shortid\"\n)\n\nfunc GenShortId() (string, error) {"
},
{
"path": "demo11/Makefile",
"chars": 572,
"preview": "all: gotool\n\t@go build -v .\nclean:\n\trm -f apiserver\n\tfind . -name \"[._]*.s[a-w][a-z]\" | xargs -i rm -f {}\ngotool:\n\tgofmt"
},
{
"path": "demo11/README.md",
"chars": 20,
"preview": "进阶:用Makefile管理API项目\n"
},
{
"path": "demo11/conf/config.yaml",
"chars": 740,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo11/conf/server.crt",
"chars": 1395,
"preview": "-----BEGIN CERTIFICATE-----\nMIID2TCCAsGgAwIBAgIJAI/8axTNy37vMA0GCSqGSIb3DQEBBQUAMIGCMQswCQYD\nVQQGEwJERTEMMAoGA1UECAwDTlJ"
},
{
"path": "demo11/conf/server.key",
"chars": 1704,
"preview": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCh256ZmsR6JBQv\nK5yZRuNni+At7aIO6337/eg68A2"
},
{
"path": "demo11/config/config.go",
"chars": 1617,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\""
},
{
"path": "demo11/db.sql",
"chars": 2210,
"preview": "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SE"
},
{
"path": "demo11/handler/handler.go",
"chars": 458,
"preview": "package handler\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype Response struct {\n\tCo"
},
{
"path": "demo11/handler/sd/check.go",
"chars": 2207,
"preview": "package sd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/cpu\"\n\t\"github.com/shir"
},
{
"path": "demo11/handler/user/create.go",
"chars": 1014,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t\"github.com/"
},
{
"path": "demo11/handler/user/delete.go",
"chars": 395,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-go"
},
{
"path": "demo11/handler/user/get.go",
"chars": 422,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n"
},
{
"path": "demo11/handler/user/list.go",
"chars": 508,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/service\"\n\n\t\"github.com/gin-gonic/gin\"\n)"
},
{
"path": "demo11/handler/user/login.go",
"chars": 1017,
"preview": "package user\n\nimport (\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/auth\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserve"
},
{
"path": "demo11/handler/user/update.go",
"chars": 1045,
"preview": "package user\n\nimport (\n\t\"strconv\"\n\n\t. \"apiserver/handler\"\n\t\"apiserver/model\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/util\"\n\n\t"
},
{
"path": "demo11/handler/user/user.go",
"chars": 469,
"preview": "package user\n\nimport (\n\t\"apiserver/model\"\n)\n\ntype CreateRequest struct {\n\tUsername string `json:\"username\"`\n\tPassword st"
},
{
"path": "demo11/main.go",
"chars": 2042,
"preview": "package main\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"apiserver/config\"\n\t\"apiserver/model\"\n\t\"apiserver/router\"\n\t\"apise"
},
{
"path": "demo11/model/init.go",
"chars": 1589,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/lexkong/log\"\n\t\"github.com/spf13/viper\"\n\t// MySQL driver.\n\t\"github.com/jinzh"
},
{
"path": "demo11/model/model.go",
"chars": 730,
"preview": "package model\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype BaseModel struct {\n\tId uint64 `gorm:\"primary_key;AUTO_INCREME"
},
{
"path": "demo11/model/user.go",
"chars": 2118,
"preview": "package model\n\nimport (\n\t\"fmt\"\n\n\t\"apiserver/pkg/auth\"\n\t\"apiserver/pkg/constvar\"\n\n\tvalidator \"gopkg.in/go-playground/vali"
},
{
"path": "demo11/pkg/auth/auth.go",
"chars": 479,
"preview": "package auth\n\nimport \"golang.org/x/crypto/bcrypt\"\n\n// Encrypt encrypts the plain text with bcrypt.\nfunc Encrypt(source s"
},
{
"path": "demo11/pkg/constvar/constvar.go",
"chars": 47,
"preview": "package constvar\n\nconst (\n\tDefaultLimit = 50\n)\n"
},
{
"path": "demo11/pkg/errno/code.go",
"chars": 887,
"preview": "package errno\n\nvar (\n\t// Common errors\n\tOK = &Errno{Code: 0, Message: \"OK\"}\n\tInternalServerError = &Err"
},
{
"path": "demo11/pkg/errno/errno.go",
"chars": 1077,
"preview": "package errno\n\nimport \"fmt\"\n\ntype Errno struct {\n\tCode int\n\tMessage string\n}\n\nfunc (err Errno) Error() string {\n\tretu"
},
{
"path": "demo11/pkg/token/token.go",
"chars": 2399,
"preview": "package token\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\tjwt \"github.com/dgrijalva/jwt-go\"\n\t\"github.com/gin-gonic/gin\"\n\t\"githu"
},
{
"path": "demo11/router/middleware/auth.go",
"chars": 380,
"preview": "package middleware\n\nimport (\n\t\"apiserver/handler\"\n\t\"apiserver/pkg/errno\"\n\t\"apiserver/pkg/token\"\n\n\t\"github.com/gin-gonic/"
},
{
"path": "demo11/router/middleware/header.go",
"chars": 1576,
"preview": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoCache is a middleware function tha"
},
{
"path": "demo11/router/middleware/logging.go",
"chars": 2016,
"preview": "package middleware\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"regexp\"\n\t\"time\"\n\n\t\"apiserver/handler\"\n\t\"apiserver/"
},
{
"path": "demo11/router/middleware/requestid.go",
"chars": 545,
"preview": "package middleware\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/satori/go.uuid\"\n)\n\nfunc RequestId() gin.HandlerFun"
},
{
"path": "demo11/router/router.go",
"chars": 1054,
"preview": "package router\n\nimport (\n\t\"net/http\"\n\n\t\"apiserver/handler/sd\"\n\t\"apiserver/handler/user\"\n\t\"apiserver/router/middleware\"\n\n"
},
{
"path": "demo11/service/service.go",
"chars": 1410,
"preview": "package service\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"apiserver/model\"\n\t\"apiserver/util\"\n)\n\nfunc ListUser(username string, offset,"
},
{
"path": "demo11/util/util.go",
"chars": 320,
"preview": "package util\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/teris-io/shortid\"\n)\n\nfunc GenShortId() (string, error) {"
},
{
"path": "demo12/Makefile",
"chars": 1230,
"preview": "SHELL := /bin/bash\nBASEDIR = $(shell pwd)\n\n# build with verison infos\nversionDir = \"apiserver/pkg/version\"\ngitTag = $(sh"
},
{
"path": "demo12/README.md",
"chars": 16,
"preview": "进阶:给API命令增加版本功能\n"
},
{
"path": "demo12/conf/config.yaml",
"chars": 740,
"preview": "runmode: debug # 开发模式, debug, release, test\naddr: :8080 # HTTP绑定端口\nname: apiserver "
},
{
"path": "demo12/conf/server.crt",
"chars": 1395,
"preview": "-----BEGIN CERTIFICATE-----\nMIID2TCCAsGgAwIBAgIJAI/8axTNy37vMA0GCSqGSIb3DQEBBQUAMIGCMQswCQYD\nVQQGEwJERTEMMAoGA1UECAwDTlJ"
}
]
// ... and 217 more files (download for full content)
About this extraction
This page contains the full source code of the lexkong/apiserver_demos GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 417 files (459.0 KB), approximately 155.1k tokens, and a symbol index with 1052 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.