Skip to content

Commit 5c46815

Browse files
committed
v0.1.0-beta
- Update git ignore files - Add log functionality - Cancel the fixed JWT key and switch to using the superuser token. - Added the ability to read env files. - Some changes were made to the setup superusertoken. - Fix known bugs
1 parent eebd345 commit 5c46815

File tree

14 files changed

+263
-44
lines changed

14 files changed

+263
-44
lines changed

.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# if set, then mean production
2+
GIN_MODE=release
3+
# if set, then mean production
4+
ENV=production
5+
PORT=3333

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# --- 敏感文件 (严禁上传) ---
22
.superusertoken
3+
.env
4+
35

46
# --- 数据库文件 ---
57
*.db
@@ -21,3 +23,4 @@ bin/
2123

2224
# --- 依赖管理 ---
2325
# vendor/ # 如果你使用 go mod vendor 且想提交,可以注释掉这行
26+

config/logger.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package config
2+
3+
import (
4+
"log/slog"
5+
"os"
6+
)
7+
8+
func InitLogger() {
9+
var handler slog.Handler
10+
11+
// 设置配置项
12+
opts := &slog.HandlerOptions{
13+
// AddSource: true 可以在日志中看到具体是哪一个文件、哪一行代码打的日志
14+
AddSource: true,
15+
// 默认级别是 Info,你可以根据环境变量动态调整
16+
Level: slog.LevelDebug,
17+
}
18+
19+
if os.Getenv("ENV") == "production" {
20+
handler = slog.NewJSONHandler(os.Stdout, opts)
21+
} else {
22+
handler = slog.NewTextHandler(os.Stdout, opts)
23+
}
24+
25+
logger := slog.New(handler)
26+
27+
// 设置为全局默认,这样你在其他 package 直接调用 slog.Info 即可
28+
slog.SetDefault(logger)
29+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ require (
2727
github.com/goccy/go-yaml v1.19.2 // indirect
2828
github.com/jinzhu/inflection v1.0.0 // indirect
2929
github.com/jinzhu/now v1.1.5 // indirect
30+
github.com/joho/godotenv v1.5.1 // indirect
3031
github.com/json-iterator/go v1.1.12 // indirect
3132
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
3233
github.com/kr/text v0.2.0 // indirect
@@ -36,12 +37,14 @@ require (
3637
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
3738
github.com/modern-go/reflect2 v1.0.2 // indirect
3839
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
40+
github.com/pkg/errors v0.9.1 // indirect
3941
github.com/pmezard/go-difflib v1.0.0 // indirect
4042
github.com/quic-go/qpack v0.6.0 // indirect
4143
github.com/quic-go/quic-go v0.58.0 // indirect
4244
github.com/stretchr/objx v0.5.3 // indirect
4345
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
4446
github.com/ugorji/go/codec v1.3.1 // indirect
47+
github.com/ulule/limiter/v3 v3.11.2 // indirect
4548
go.uber.org/mock v0.6.0 // indirect
4649
golang.org/x/arch v0.23.0 // indirect
4750
golang.org/x/net v0.48.0 // indirect

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
3939
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
4040
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
4141
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
42+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
43+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
4244
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
4345
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
4446
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
@@ -60,6 +62,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
6062
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
6163
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
6264
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
65+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
66+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
6367
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
6468
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6569
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
@@ -86,6 +90,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
8690
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
8791
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
8892
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
93+
github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA=
94+
github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI=
8995
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
9096
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
9197
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=

handlers/announcement_handler.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package handlers
22

33
import (
44
"errors"
5+
"log/slog"
56
"net/http"
67

78
"github.com/gin-gonic/gin"
@@ -27,8 +28,8 @@ func (h *AnnouncementHandler) GetOne(c *gin.Context) {
2728
c.JSON(http.StatusNotFound, gin.H{"message": "Announcement not found"})
2829
return
2930
}
30-
// 这里的打印可以保留,方便服务器排查
31-
println("[Server Error] Query Error: " + err.Error())
31+
32+
slog.Error("Database query error", "handler", "GetOne", "id", id, "err", err)
3233
c.JSON(http.StatusInternalServerError, gin.H{"message": "Something went wrong"})
3334
return
3435
}
@@ -39,6 +40,7 @@ func (h *AnnouncementHandler) GetOne(c *gin.Context) {
3940
func (h *AnnouncementHandler) GetAll(c *gin.Context) {
4041
announcements, err := h.Service.GetAll()
4142
if err != nil {
43+
slog.Error("Failed to fetch announcements", "err", err)
4244
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
4345
return
4446
}
@@ -50,13 +52,15 @@ func (h *AnnouncementHandler) Create(c *gin.Context) {
5052
var input models.CreateAnnouncementRequest
5153

5254
if err := c.ShouldBindJSON(&input); err != nil {
55+
slog.Debug("Create announcement bind failed", "err", err) // DEBUG 即可,不干扰生产
5356
c.JSON(400, gin.H{"error": err.Error()})
5457
return
5558
}
5659

5760
val, exists := c.Get("currentUserID")
5861

5962
if !exists {
63+
slog.Error("Create announcement obtain current user information failed", "err", "Unable to obtain current user information")
6064
c.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to obtain current user information"})
6165
return
6266
}
@@ -68,10 +72,18 @@ func (h *AnnouncementHandler) Create(c *gin.Context) {
6872
}
6973

7074
if err := h.Service.Create(&announcement); err != nil {
75+
slog.Error("Create announcement failed", "err", err.Error())
76+
7177
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
7278
return
7379
}
7480

81+
// 在 c.JSON 成功返回之前
82+
slog.Info("Announcement created",
83+
"id", announcement.ID,
84+
"author_id", announcement.AuthorID,
85+
"title", announcement.Title)
86+
7587
c.JSON(http.StatusCreated, gin.H{
7688
"message": "Created",
7789
"data": announcement,
@@ -92,6 +104,9 @@ func (h *AnnouncementHandler) Update(c *gin.Context) {
92104
}
93105

94106
if announcement.AuthorID != val {
107+
slog.Warn("Unauthorized update attempt",
108+
"announcement_id", id,
109+
"user_id", val)
95110
c.JSON(http.StatusForbidden, gin.H{"error": "You do not have permission to modify other people's announcements."})
96111
return
97112
}
@@ -106,10 +121,13 @@ func (h *AnnouncementHandler) Update(c *gin.Context) {
106121
announcement.Content = input.Content
107122

108123
if err := h.Service.Update(announcement); err != nil {
124+
slog.Error("Announcement failed", "id", id, "operator_id", val, "err", err.Error())
109125
c.JSON(http.StatusInternalServerError, gin.H{"error": "Update Failed"})
110126
return
111127
}
112128

129+
slog.Info("Announcement updated", "id", id, "operator_id", val)
130+
113131
c.JSON(http.StatusOK, announcement)
114132

115133
}
@@ -125,16 +143,20 @@ func (h *AnnouncementHandler) Delete(c *gin.Context) {
125143
}
126144

127145
if announcement.AuthorID != val {
146+
slog.Warn("Unauthorized delete attempt", "id", id, "user_id", val)
128147
c.JSON(http.StatusForbidden, gin.H{"error": "You do not have permission to modify other people's announcements."})
129148
return
130149
}
131150

132151
err = h.Service.Delete(id)
133152
if err != nil {
153+
slog.Error("Delete Announcement Failed", "id", id, "user_id", val, "err", err.Error())
134154
c.JSON(http.StatusInternalServerError, gin.H{"error": "Cannot Delete"})
135155
return
136156
}
137157

158+
slog.Info("Announcement deleted", "id", id, "operator_id", val)
159+
138160
c.JSON(http.StatusOK, gin.H{"message": "deleted"})
139161

140162
return

handlers/auth_handler.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package handlers
22

33
import (
4+
"log/slog"
5+
46
"github.com/gin-gonic/gin"
57
"pdnode.com/website/models"
68
"pdnode.com/website/services"
@@ -14,16 +16,32 @@ type AuthHandler struct {
1416
func (h *AuthHandler) Login(c *gin.Context) {
1517
var input models.LoginRequest
1618
if err := c.ShouldBindJSON(&input); err != nil {
19+
slog.Debug("request bind failed",
20+
"path", c.Request.URL.Path,
21+
"error", err.Error(),
22+
"ip", c.ClientIP(),
23+
)
1724
c.JSON(400, gin.H{"error": err.Error()})
1825
return
1926
}
2027

2128
user, token, err := h.Service.Login(input.Email, input.Password)
2229
if err != nil {
30+
slog.Warn("Login failed",
31+
"email", input.Email,
32+
"reason", "invalid credentials", // 明确失败原因
33+
"ip", c.ClientIP(),
34+
)
2335
c.JSON(401, gin.H{"error": err.Error()})
2436
return
2537
}
2638

39+
slog.Info("user login success",
40+
"email", user.Email,
41+
"user_id", user.ID,
42+
"ip", c.ClientIP(),
43+
)
44+
2745
c.JSON(200, gin.H{"message": "success", "token": token, "user": user})
2846
}
2947

main.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
package main
22

33
import (
4+
"log"
5+
"log/slog"
6+
"os"
7+
48
"github.com/gin-gonic/gin"
9+
"github.com/joho/godotenv"
510
"gorm.io/driver/sqlite"
611
"gorm.io/gorm"
12+
"pdnode.com/website/config"
13+
"pdnode.com/website/middleware"
714
"pdnode.com/website/models"
815
"pdnode.com/website/routes"
16+
"pdnode.com/website/utils"
917
)
1018

1119
// Setup JWT Secret Key
@@ -14,9 +22,25 @@ import (
1422

1523
func main() {
1624

25+
err := godotenv.Load()
26+
if err != nil {
27+
log.Fatal("Error loading .env file")
28+
}
29+
1730
// Setup DB
1831
db, err := gorm.Open(sqlite.Open("data.db"), &gorm.Config{})
1932

33+
config.InitLogger()
34+
35+
if os.Getenv("ENV") == "production" {
36+
gin.SetMode(gin.ReleaseMode) // 这样 [GIN-debug] 就全消失了
37+
}
38+
39+
slog.Info("Environment Check",
40+
"ENV_VAR", os.Getenv("ENV"),
41+
"PORT_VAR", os.Getenv("PORT"),
42+
)
43+
2044
if err != nil {
2145
panic("failed to connect database")
2246
}
@@ -26,13 +50,21 @@ func main() {
2650
panic("failed to migrate database: " + err.Error())
2751
}
2852

53+
// 4. 创建 Gin 实例 (不使用 Default 以免日志冲突)
54+
r := gin.New()
2955
gin.ForceConsoleColor()
30-
r := routes.SetupRouter(db)
56+
57+
r.Use(middleware.LoggerMiddleware())
58+
r.Use(gin.Recovery()) // 建议加上,防止 panic 导致程序退出
3159

3260
SetUpSuperuser()
3361

34-
err = r.Run()
35-
if err != nil {
62+
utils.InitAuth()
63+
64+
routes.SetupRouter(r, db)
65+
66+
// 8. 启动
67+
if err := r.Run(); err != nil {
3668
panic(err)
3769
}
3870

middleware/logger.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package middleware
2+
3+
import (
4+
"log/slog"
5+
"time"
6+
7+
"github.com/gin-gonic/gin"
8+
)
9+
10+
func LoggerMiddleware() gin.HandlerFunc {
11+
return func(c *gin.Context) {
12+
start := time.Now()
13+
path := c.Request.URL.Path
14+
query := c.Request.URL.RawQuery
15+
16+
// 处理请求
17+
c.Next()
18+
19+
// 执行完 Handler 后记录信息
20+
end := time.Now()
21+
latency := end.Sub(start)
22+
23+
slog.Info("HTTP Request",
24+
"status", c.Writer.Status(),
25+
"method", c.Request.Method,
26+
"path", path,
27+
"query", query,
28+
"ip", c.ClientIP(),
29+
"latency", latency.String(),
30+
"user-agent", c.Request.UserAgent(),
31+
)
32+
}
33+
}

0 commit comments

Comments
 (0)