Skip to content

Commit abff5b9

Browse files
committed
update: all in one feature
1 parent aa627fb commit abff5b9

File tree

8 files changed

+374
-9
lines changed

8 files changed

+374
-9
lines changed

auth/auth.go

+159-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package auth
22

33
import (
4+
"chat/channel"
45
"chat/globals"
56
"chat/utils"
67
"database/sql"
78
"errors"
89
"fmt"
910
"github.com/dgrijalva/jwt-go"
1011
"github.com/gin-gonic/gin"
12+
"github.com/go-redis/redis/v8"
1113
"github.com/spf13/viper"
14+
"strings"
1215
"time"
1316
)
1417

@@ -54,9 +57,129 @@ func ParseApiKey(c *gin.Context, key string) *User {
5457
return &user
5558
}
5659

60+
func getCode(c *gin.Context, cache *redis.Client, email string) string {
61+
code, err := cache.Get(c, fmt.Sprintf("nio:otp:%s", email)).Result()
62+
if err != nil {
63+
return ""
64+
}
65+
return code
66+
}
67+
68+
func checkCode(c *gin.Context, cache *redis.Client, email, code string) bool {
69+
storage := getCode(c, cache, email)
70+
if len(storage) == 0 {
71+
return false
72+
}
73+
74+
if storage != code {
75+
return false
76+
}
77+
78+
cache.Del(c, fmt.Sprintf("nio:top:%s", email))
79+
return true
80+
}
81+
82+
func setCode(c *gin.Context, cache *redis.Client, email, code string) {
83+
cache.Set(c, fmt.Sprintf("nio:otp:%s", email), code, 5*time.Minute)
84+
}
85+
86+
func generateCode(c *gin.Context, cache *redis.Client, email string) string {
87+
code := utils.GenerateCode(6)
88+
setCode(c, cache, email, code)
89+
return code
90+
}
91+
92+
func Verify(c *gin.Context, email string) error {
93+
cache := utils.GetCacheFromContext(c)
94+
code := generateCode(c, cache, email)
95+
96+
provider := channel.SystemInstance.GetMail()
97+
return provider.SendMail(
98+
email,
99+
"Chat Nio | OTP Verification",
100+
fmt.Sprintf("Your OTP code is: %s", code),
101+
)
102+
}
103+
104+
func SignUp(c *gin.Context, form RegisterForm) (string, error) {
105+
db := utils.GetDBFromContext(c)
106+
cache := utils.GetCacheFromContext(c)
107+
108+
username := strings.TrimSpace(form.Username)
109+
password := strings.TrimSpace(form.Password)
110+
email := strings.TrimSpace(form.Email)
111+
code := strings.TrimSpace(form.Code)
112+
113+
if !utils.All(
114+
validateUsername(username),
115+
validatePassword(password),
116+
validateEmail(email),
117+
validateCode(code),
118+
) {
119+
return "", errors.New("invalid username/password/email format")
120+
}
121+
122+
if !IsUserExist(db, username) {
123+
return "", fmt.Errorf("username is already taken, please try another one username (your current username: %s)", username)
124+
}
125+
126+
if !IsEmailExist(db, email) {
127+
return "", fmt.Errorf("email is already taken, please try another one email (your current email: %s)", email)
128+
}
129+
130+
if !checkCode(c, cache, email, code) {
131+
return "", errors.New("invalid email verification code")
132+
}
133+
134+
hash := utils.Sha2Encrypt(password)
135+
136+
user := &User{
137+
Username: username,
138+
Password: hash,
139+
Email: email,
140+
BindID: getMaxBindId(db) + 1,
141+
Token: utils.Sha2Encrypt(email + username),
142+
}
143+
144+
if _, err := db.Exec(`
145+
INSERT INTO auth (username, password, email, bind_id, token)
146+
VALUES (?, ?, ?, ?, ?)
147+
`, user.Username, user.Password, user.Email, user.BindID, user.Token); err != nil {
148+
return "", err
149+
}
150+
151+
return user.GenerateToken()
152+
}
153+
154+
func Login(c *gin.Context, form LoginForm) (string, error) {
155+
db := utils.GetDBFromContext(c)
156+
username := strings.TrimSpace(form.Username)
157+
password := strings.TrimSpace(form.Password)
158+
159+
if !utils.All(
160+
validateUsernameOrEmail(username),
161+
validatePassword(password),
162+
) {
163+
return "", errors.New("invalid username or password format")
164+
}
165+
166+
hash := utils.Sha2Encrypt(password)
167+
168+
// get user from db by username (or email) and password
169+
var user User
170+
if err := db.QueryRow(`
171+
SELECT auth.id, auth.username, auth.password FROM auth
172+
WHERE (auth.username = ? OR auth.email = ?) AND auth.password = ?
173+
`, username, hash).Scan(&user.ID, &user.Username, &user.Password); err != nil {
174+
return "", errors.New("invalid username or password")
175+
}
176+
177+
return user.GenerateToken()
178+
}
179+
57180
func DeepLogin(c *gin.Context, token string) (string, error) {
58181
if !useDeeptrain() {
59-
return "", errors.New("deeptrain feature is disabled")
182+
return "", errors.New("deeptrain mode is disabled")
60183
}
61184

62185
user := Validate(token)
@@ -91,6 +214,41 @@ func DeepLogin(c *gin.Context, token string) (string, error) {
91214
return u.GenerateToken()
92215
}
93216

217+
func Reset(c *gin.Context, form ResetForm) error {
218+
db := utils.GetDBFromContext(c)
219+
cache := utils.GetCacheFromContext(c)
220+
221+
email := strings.TrimSpace(form.Email)
222+
code := strings.TrimSpace(form.Code)
223+
password := strings.TrimSpace(form.Password)
224+
225+
if !utils.All(
226+
validateEmail(email),
227+
validateCode(code),
228+
validatePassword(password),
229+
) {
230+
return errors.New("invalid email/code/password format")
231+
}
232+
233+
if !IsEmailExist(db, email) {
234+
return errors.New("email is not registered")
235+
}
236+
237+
if !checkCode(c, cache, email, code) {
238+
return errors.New("invalid email verification code")
239+
}
240+
241+
hash := utils.Sha2Encrypt(password)
242+
243+
if _, err := db.Exec(`
244+
UPDATE auth SET password = ? WHERE email = ?
245+
`, hash, email); err != nil {
246+
return err
247+
}
248+
249+
return nil
250+
}
251+
94252
func (u *User) Validate(c *gin.Context) bool {
95253
if u.Username == "" || u.Password == "" {
96254
return false

auth/controller.go

+121-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,32 @@ import (
77
"strings"
88
)
99

10+
type RegisterForm struct {
11+
Username string `form:"username" binding:"required"`
12+
Password string `form:"password" binding:"required"`
13+
Email string `form:"email" binding:"required"`
14+
Code string `form:"code" binding:"required"`
15+
}
16+
17+
type VerifyForm struct {
18+
Email string `form:"email" binding:"required"`
19+
}
20+
21+
type LoginForm struct {
22+
Username string `form:"username" binding:"required"`
23+
Password string `form:"password" binding:"required"`
24+
}
25+
1026
type DeepLoginForm struct {
1127
Token string `form:"token" binding:"required"`
1228
}
1329

30+
type ResetForm struct {
31+
Email string `form:"email" binding:"required"`
32+
Code string `form:"code" binding:"required"`
33+
Password string `form:"password" binding:"required"`
34+
}
35+
1436
type BuyForm struct {
1537
Quota int `json:"quota" binding:"required"`
1638
}
@@ -111,8 +133,16 @@ func RequireEnterprise(c *gin.Context) *User {
111133
return user
112134
}
113135

114-
func LoginAPI(c *gin.Context) {
115-
var form DeepLoginForm
136+
func RegisterAPI(c *gin.Context) {
137+
if useDeeptrain() {
138+
c.JSON(http.StatusOK, gin.H{
139+
"status": false,
140+
"error": "this api is not available for deeptrain mode",
141+
})
142+
return
143+
}
144+
145+
var form RegisterForm
116146
if err := c.ShouldBind(&form); err != nil {
117147
c.JSON(http.StatusOK, gin.H{
118148
"status": false,
@@ -121,7 +151,7 @@ func LoginAPI(c *gin.Context) {
121151
return
122152
}
123153

124-
token, err := DeepLogin(c, form.Token)
154+
token, err := SignUp(c, form)
125155
if err != nil {
126156
c.JSON(http.StatusOK, gin.H{
127157
"status": false,
@@ -136,6 +166,94 @@ func LoginAPI(c *gin.Context) {
136166
})
137167
}
138168

169+
func LoginAPI(c *gin.Context) {
170+
var token string
171+
var err error
172+
173+
if useDeeptrain() {
174+
var form DeepLoginForm
175+
if err := c.ShouldBind(&form); err != nil {
176+
c.JSON(http.StatusOK, gin.H{
177+
"status": false,
178+
"error": "bad request",
179+
})
180+
return
181+
}
182+
183+
token, err = DeepLogin(c, form.Token)
184+
} else {
185+
var form LoginForm
186+
if err := c.ShouldBind(&form); err != nil {
187+
c.JSON(http.StatusOK, gin.H{
188+
"status": false,
189+
"error": "bad request",
190+
})
191+
return
192+
}
193+
194+
token, err = Login(c, form)
195+
}
196+
197+
if err != nil {
198+
c.JSON(http.StatusOK, gin.H{
199+
"status": false,
200+
"error": err.Error(),
201+
})
202+
return
203+
}
204+
205+
c.JSON(http.StatusOK, gin.H{
206+
"status": true,
207+
"token": token,
208+
})
209+
}
210+
211+
func VerifyAPI(c *gin.Context) {
212+
var form VerifyForm
213+
if err := c.ShouldBind(&form); err != nil {
214+
c.JSON(http.StatusOK, gin.H{
215+
"status": false,
216+
"error": "bad request",
217+
})
218+
return
219+
}
220+
221+
if err := Verify(c, form.Email); err != nil {
222+
c.JSON(http.StatusOK, gin.H{
223+
"status": false,
224+
"error": err.Error(),
225+
})
226+
return
227+
}
228+
229+
c.JSON(http.StatusOK, gin.H{
230+
"status": true,
231+
})
232+
}
233+
234+
func ResetAPI(c *gin.Context) {
235+
var form ResetForm
236+
if err := c.ShouldBind(&form); err != nil {
237+
c.JSON(http.StatusOK, gin.H{
238+
"status": false,
239+
"error": "bad request",
240+
})
241+
return
242+
}
243+
244+
if err := Reset(c, form); err != nil {
245+
c.JSON(http.StatusOK, gin.H{
246+
"status": false,
247+
"error": err.Error(),
248+
})
249+
return
250+
}
251+
252+
c.JSON(http.StatusOK, gin.H{
253+
"status": true,
254+
})
255+
}
256+
139257
func StateAPI(c *gin.Context) {
140258
username := utils.GetUserFromContext(c)
141259
c.JSON(http.StatusOK, gin.H{

auth/router.go

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package auth
33
import "github.com/gin-gonic/gin"
44

55
func Register(app *gin.Engine) {
6+
app.POST("/verify", VerifyAPI)
7+
app.POST("/reset", ResetAPI)
8+
app.POST("/register", RegisterAPI)
69
app.POST("/login", LoginAPI)
710
app.POST("/state", StateAPI)
811
app.GET("/apikey", KeyAPI)

0 commit comments

Comments
 (0)