Skip to content

Commit 9f6fcad

Browse files
committed
v0.1.0-alpha
- Reorganize the directory - Add Create New Announcement - Add hashes to password and superusertoken - Fix known bugs
1 parent 9871ab5 commit 9f6fcad

15 files changed

Lines changed: 498 additions & 206 deletions

File tree

handlers/announcement_handler.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package handlers
2+
3+
import (
4+
"errors"
5+
"net/http"
6+
7+
"github.com/gin-gonic/gin"
8+
"github.com/google/uuid"
9+
"gorm.io/gorm"
10+
"pdnode.com/website/models"
11+
"pdnode.com/website/services"
12+
)
13+
14+
type AnnouncementHandler struct {
15+
Service *services.AnnouncementService
16+
}
17+
18+
func (h *AnnouncementHandler) GetOne(c *gin.Context) {
19+
id := c.Param("id")
20+
21+
a, err := h.Service.GetByID(id)
22+
if err != nil {
23+
if errors.Is(err, gorm.ErrRecordNotFound) {
24+
c.JSON(http.StatusNotFound, gin.H{"message": "Announcement not found"})
25+
return
26+
}
27+
// 这里的打印可以保留,方便服务器排查
28+
println("[Server Error] Query Error: " + err.Error())
29+
c.JSON(http.StatusInternalServerError, gin.H{"message": "Something went wrong"})
30+
return
31+
}
32+
33+
c.JSON(http.StatusOK, a)
34+
}
35+
36+
func (h *AnnouncementHandler) GetAll(c *gin.Context) {
37+
announcements, err := h.Service.GetAll()
38+
if err != nil {
39+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
40+
return
41+
}
42+
43+
c.JSON(http.StatusOK, announcements)
44+
}
45+
46+
func (h *AnnouncementHandler) Create(c *gin.Context) {
47+
var input models.CreateAnnouncementRequest
48+
49+
if err := c.ShouldBindJSON(&input); err != nil {
50+
c.JSON(400, gin.H{"error": err.Error()})
51+
return
52+
}
53+
54+
val, exists := c.Get("currentUserID")
55+
56+
if !exists {
57+
c.JSON(500, gin.H{"error": "Unable to obtain current user information"})
58+
return
59+
}
60+
61+
announcement := models.Announcement{
62+
Title: input.Title,
63+
Content: input.Content,
64+
AuthorID: val.(uuid.UUID),
65+
}
66+
67+
if err := h.Service.Create(&announcement); err != nil {
68+
c.JSON(400, gin.H{"error": err.Error()})
69+
return
70+
}
71+
72+
c.JSON(201, gin.H{
73+
"message": "Created",
74+
"data": announcement,
75+
})
76+
77+
}

handlers/auth_handler.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package handlers
2+
3+
import (
4+
"github.com/gin-gonic/gin"
5+
"pdnode.com/website/models"
6+
"pdnode.com/website/services"
7+
"pdnode.com/website/utils"
8+
)
9+
10+
type AuthHandler struct {
11+
Service *services.AuthService
12+
}
13+
14+
func (h *AuthHandler) Login(c *gin.Context) {
15+
var input models.LoginRequest
16+
if err := c.ShouldBindJSON(&input); err != nil {
17+
c.JSON(400, gin.H{"error": err.Error()})
18+
return
19+
}
20+
21+
user, token, err := h.Service.Login(input.Email, input.Password)
22+
if err != nil {
23+
c.JSON(401, gin.H{"error": err.Error()})
24+
return
25+
}
26+
27+
c.JSON(200, gin.H{"message": "success", "token": token, "user": user})
28+
}
29+
30+
func (h *AuthHandler) Register(c *gin.Context) {
31+
var input models.RegisterRequest
32+
if err := c.ShouldBindJSON(&input); err != nil {
33+
c.JSON(400, gin.H{"error": err.Error()})
34+
return
35+
}
36+
37+
// Handler 负责处理 Header 这种 HTTP 特有的东西
38+
if c.GetHeader("X-Super-Token") != string(utils.GetSuperuserToken()) {
39+
c.JSON(403, gin.H{"error": "Invalid token"})
40+
return
41+
}
42+
43+
if err := h.Service.Register(input); err != nil {
44+
c.JSON(400, gin.H{"error": err.Error()})
45+
return
46+
}
47+
48+
c.JSON(201, gin.H{"message": "success"})
49+
}

main.go

Lines changed: 5 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,16 @@
11
package main
22

33
import (
4-
"errors"
5-
64
"github.com/gin-gonic/gin"
7-
"golang.org/x/crypto/bcrypt"
85
"gorm.io/driver/sqlite"
96
"gorm.io/gorm"
7+
"pdnode.com/website/models"
8+
"pdnode.com/website/routes"
109
)
1110

1211
// Setup JWT Secret Key
13-
var jwtKey = []byte("your_secret_key_pdnode")
1412

15-
// HashPassword 1. 加密密码(注册时使用)
16-
func HashPassword(password string) (string, error) {
17-
// 强度系数默认为 10,数值越大越慢(越安全)
18-
combined := append([]byte(password), GetSuperuserToken()...)
19-
bytes, err := bcrypt.GenerateFromPassword(combined, bcrypt.DefaultCost)
20-
return string(bytes), err
21-
}
22-
23-
// CheckPasswordHash 2. 比对密码(登录时使用)
24-
func CheckPasswordHash(password, hash string) bool {
25-
combined := append([]byte(password), GetSuperuserToken()...)
26-
err := bcrypt.CompareHashAndPassword([]byte(hash), combined)
27-
return err == nil
28-
}
13+
// HashPassword 改进版(带 Pre-hash)
2914

3015
func main() {
3116

@@ -36,149 +21,13 @@ func main() {
3621
panic("failed to connect database")
3722
}
3823

39-
err = db.AutoMigrate(&Announcement{}, &User{})
24+
err = db.AutoMigrate(&models.Announcement{}, &models.User{})
4025
if err != nil {
4126
panic("failed to migrate database: " + err.Error())
4227
}
4328

4429
gin.ForceConsoleColor()
45-
r := gin.Default()
46-
47-
r.GET("/", func(c *gin.Context) {
48-
c.JSON(200, gin.H{
49-
"msg": "Pdnode Website API is running",
50-
})
51-
})
52-
53-
r.GET("/announcements/:id", func(c *gin.Context) {
54-
id := c.Param("id")
55-
56-
var a Announcement
57-
58-
result := db.First(&a, id)
59-
60-
if result.Error != nil {
61-
62-
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
63-
c.JSON(404, gin.H{
64-
"message": "Announcement not found",
65-
})
66-
}
67-
68-
c.JSON(500, gin.H{
69-
"message": "Something went wrong",
70-
})
71-
72-
println("[Server Error] Query Error: " + result.Error.Error())
73-
74-
return
75-
}
76-
77-
c.JSON(200, a)
78-
return
79-
80-
})
81-
82-
r.GET("/announcements", func(c *gin.Context) {
83-
var a []Announcement
84-
result := db.Order("created_at desc").Find(&a)
85-
if result.Error != nil {
86-
c.JSON(500, gin.H{
87-
"error": result.Error.Error(),
88-
})
89-
return
90-
}
91-
92-
c.JSON(200, a)
93-
94-
})
95-
96-
r.POST("/login", func(c *gin.Context) {
97-
var input LoginRequest
98-
99-
if err := c.ShouldBind(&input); err != nil {
100-
c.JSON(400, gin.H{
101-
"error": err.Error(),
102-
})
103-
return
104-
}
105-
106-
var user User
107-
const fakeBCryptHash = "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgNIhp.qfTMvYeJLvAbpE52EPIvG"
108-
109-
result := db.Where("email = ?", input.Email).First(&user)
110-
111-
if result.Error != nil {
112-
CheckPasswordHash(input.Password, fakeBCryptHash)
113-
c.JSON(404, gin.H{"error": "Incorrect credentials"})
114-
return
115-
}
116-
117-
isMatch := CheckPasswordHash(input.Password, user.Password)
118-
119-
if !isMatch {
120-
c.JSON(401, gin.H{"error": "Incorrect credentials"})
121-
return
122-
}
123-
124-
token, err := GenerateToken(user.ID)
125-
if err != nil {
126-
c.JSON(500, gin.H{"error": "Your credentials are correct, but the access key generation is incorrect."})
127-
return
128-
}
129-
130-
c.JSON(200, gin.H{
131-
"message": "success",
132-
"token": token, // 把这个发给前端
133-
})
134-
135-
})
136-
137-
r.POST("/register", func(c *gin.Context) {
138-
var input RegisterRequest
139-
140-
if err := c.ShouldBind(&input); err != nil {
141-
c.JSON(400, gin.H{
142-
"error": err.Error(),
143-
})
144-
return
145-
}
146-
147-
superUserToken := c.GetHeader("X-Super-Token")
148-
149-
if superUserToken == "" {
150-
c.JSON(403, gin.H{"error": "Missing token"})
151-
return
152-
}
153-
154-
if string(GetSuperuserToken()) != superUserToken {
155-
c.JSON(403, gin.H{"error": "Invalid token"})
156-
return
157-
}
158-
159-
var count int64
160-
// 只统计数量,不查询具体内容,速度更快
161-
db.Model(&User{}).Where("email = ?", input.Email).Count(&count)
162-
163-
if count > 0 {
164-
c.JSON(400, gin.H{"error": "Email already taken"})
165-
return
166-
}
167-
168-
newUser := User{
169-
Name: input.Name,
170-
Email: input.Email,
171-
Password: input.Password, // 记得用你之前的加密逻辑
172-
}
173-
174-
db.Create(&newUser)
175-
176-
c.JSON(201, gin.H{
177-
"message": "success",
178-
})
179-
return
180-
181-
})
30+
r := routes.SetupRouter(db)
18231

18332
SetUpSuperuser()
18433

middleware/auth.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package middleware
2+
3+
import (
4+
"strings"
5+
6+
"github.com/gin-gonic/gin"
7+
"pdnode.com/website/utils"
8+
)
9+
10+
func SuperuserAuth() gin.HandlerFunc {
11+
return func(c *gin.Context) {
12+
token := c.GetHeader("X-Super-Token")
13+
14+
// 校验 Token
15+
if token == "" || token != string(utils.GetSuperuserToken()) {
16+
c.JSON(401, gin.H{"error": "X-Super-Token does not provide or has an incorrect token."})
17+
18+
c.Abort()
19+
return
20+
}
21+
22+
c.Next()
23+
}
24+
}
25+
26+
func LoginAuth() gin.HandlerFunc {
27+
return func(c *gin.Context) {
28+
token := c.GetHeader("Authorization")
29+
30+
if !strings.HasPrefix(token, "Bearer ") || token == "" {
31+
c.JSON(401, gin.H{
32+
"error": "A Bearer type token is required.",
33+
})
34+
c.Abort()
35+
return
36+
}
37+
38+
token = strings.TrimSpace(strings.TrimPrefix(token, "Bearer "))
39+
40+
if token == "" {
41+
c.JSON(401, gin.H{"error": "Token is empty."})
42+
c.Abort()
43+
return
44+
}
45+
46+
userID, err := utils.VerifyToken(token)
47+
48+
if err != nil {
49+
// 如果有错误,说明验证失败
50+
c.JSON(401, gin.H{
51+
"error": err.Error(), // 这里的 err.Error() 会得到 "invalid token" 等信息
52+
})
53+
c.Abort()
54+
return
55+
}
56+
57+
c.Set("currentUserID", userID)
58+
c.Next()
59+
}
60+
}

0 commit comments

Comments
 (0)