diff --git a/frontend/src/api.js b/frontend/src/api.js index 4689f42..d48bbf8 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -35,6 +35,12 @@ export default { UserRegister(data) { return ajax('user/register', 'post', { data }) }, + UserForgetRequest(data) { + return ajax('user/forget', 'post', { data }) + }, + UserForgerPassword(data) { + return ajax('user/password', 'put', { data }) + }, UserResetPassword(data) { return ajax('user/password', 'post', { data }) }, diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 3bf0b9a..9b47874 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -1,8 +1,10 @@ import AppLayout from '@/layout/AppLayout.vue'; import { createRouter, createWebHistory } from 'vue-router'; +import Forget from '@/views/Forget.vue'; import Login from '@/views/Login.vue'; import Register from '@/views/Register.vue'; +import Reset from '@/views/Reset.vue'; import Verify from '@/views/Verify.vue'; import InstanceCreate from '@/views/users/InstanceCreate.vue'; @@ -33,6 +35,16 @@ const router = createRouter({ name: 'register', component: Register }, + { + path: '/forget', + name: 'forget', + component: Forget + }, + { + path: '/reset/:code', + name: 'forget-reset', + component: Reset + }, { path: '/verify/:code', name: 'verify', diff --git a/frontend/src/views/Forget.vue b/frontend/src/views/Forget.vue new file mode 100644 index 0000000..c9bd384 --- /dev/null +++ b/frontend/src/views/Forget.vue @@ -0,0 +1,71 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue index 3d24506..5c9dd86 100644 --- a/frontend/src/views/Login.vue +++ b/frontend/src/views/Login.vue @@ -25,7 +25,8 @@
没有账号?立即注册 - 忘记密码 + 忘记密码
@@ -69,6 +70,10 @@ const handleSubmit = () => { const handleRegister = () => { router.push('/register') } + +const handleForget = () => { + router.push('/forget') +} \ No newline at end of file diff --git a/routers/api/v1/admin/users/modify.go b/routers/api/v1/admin/users/modify.go index 4935e3b..992944f 100644 --- a/routers/api/v1/admin/users/modify.go +++ b/routers/api/v1/admin/users/modify.go @@ -49,7 +49,7 @@ func modifyHandler(ctx iris.Context) { } if req.Email != nil { - if *req.Email != "" && utils.EmailFormat(*req.Email) { + if *req.Email != "" && !utils.EmailFormat(*req.Email) { user.Email = *req.Email } } diff --git a/routers/api/v1/user/forgerPassword.go b/routers/api/v1/user/forgerPassword.go new file mode 100644 index 0000000..92e0b87 --- /dev/null +++ b/routers/api/v1/user/forgerPassword.go @@ -0,0 +1,78 @@ +package user + +import ( + "megrez/models" + "megrez/routers/api/v1/middleware" + "megrez/services/database" + "megrez/services/redis" + + "github.com/kataras/iris/v12" +) + +type forgerPasswordStruct struct { + Code string `json:"code"` + Password string `json:"password"` + RePassword string `json:"repassword"` +} + +func forgetPasswordHandler(ctx iris.Context) { + l.SetFunction("forgetPasswordHandler") + + var req forgerPasswordStruct + if err := ctx.ReadJSON(&req); err != nil { + middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest) + return + } + + if req.Code == "" || req.Password == "" || req.RePassword == "" { + middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest) + return + } + + if req.Password != req.RePassword { + middleware.Error(ctx, middleware.CodePasswordNotMatch, iris.StatusBadRequest) + return + } + + rdb := redis.RawDB + v := rdb.Get(ctx, forgetPasswordRedisKeyPrefix+req.Code) + + if v.Err() != nil { + middleware.Error(ctx, middleware.CodeUserVerifyInvalid, iris.StatusBadRequest) + return + } + + r := rdb.Del(ctx, forgetPasswordRedisKeyPrefix+req.Code) + if r.Err() != nil { + middleware.Error(ctx, middleware.CodeServeBusy, iris.StatusInternalServerError) + l.Error("delete redis verify code error: %v", r.Err()) + return + } + + id, err := v.Int() + if err != nil { + middleware.Error(ctx, middleware.CodeUserVerifyInvalid, iris.StatusBadRequest) + return + } + + user := models.Users{ + ID: uint(id), + } + result := database.DB.First(&user) + if result.Error != nil { + l.Error("get user error: %v", result.Error) + middleware.Error(ctx, middleware.CodeUserVerifyInvalid, iris.StatusBadRequest) + return + } + + user.Password = user.PasswordHash(req.Password) + + result = database.DB.Model(&user).Update("password", user.Password) + if result.Error != nil { + middleware.Error(ctx, middleware.CodeServeBusy, iris.StatusInternalServerError) + l.Error("update user password Error: %v", result.Error) + return + } + + middleware.Success(ctx) +} diff --git a/routers/api/v1/user/forgetSend.go b/routers/api/v1/user/forgetSend.go new file mode 100644 index 0000000..2457a84 --- /dev/null +++ b/routers/api/v1/user/forgetSend.go @@ -0,0 +1,97 @@ +package user + +import ( + "fmt" + "megrez/libs/crypto" + "megrez/models" + "megrez/routers/api/v1/middleware" + "megrez/services/config" + "megrez/services/database" + "megrez/services/redis" + "megrez/services/smtp" + "time" + + "github.com/kataras/iris/v12" +) + +type forgetSendStruct struct { + Email string `json:"email"` +} + +const forgetPasswordRedisKeyPrefix = "forget:user:" +const forgetPasswordUrlPrefix = "/reset/" +const forgetPasswordTitle = "重置密码" +const forgetPasswordHTMLFormat = ` +
+ + + + + + + + + + +
+ MEGREZ 天权算能聚联计算平台
+
+

Hello, %s:

+

+ 请在15分钟内点击链接: %s  进行密码重置操作,十五分钟后该链接将会失效. +

+

+ 为了保护你的账户,请不要使用单一的密码来进行重置。 +

+

+ 如果您有任何问题,请联系系统管理员以获得更多信息与支持。 +

+

+

MEGREZ 天权算能聚联计算平台

+
+
+
+` + +func forgetSendHandler(ctx iris.Context) { + var req forgetSendStruct + if err := ctx.ReadJSON(&req); err != nil { + middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest) + return + } + + if req.Email == "" { + middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest) + return + } + + user := models.Users{ + Email: req.Email, + } + result := database.DB.Where(&user).First(&user) + if result.Error != nil { + l.Error("get user error: %v", result.Error) + middleware.Error(ctx, middleware.CodeUserNotExist, iris.StatusInternalServerError) + return + } + + rdb := redis.RawDB + + forgetUrl := crypto.Hex(32) + err := rdb.Set(ctx, forgetPasswordRedisKeyPrefix+forgetUrl, user.ID, 15*time.Minute).Err() + if err != nil { + middleware.Error(ctx, middleware.CodeServeBusy, iris.StatusInternalServerError) + l.Error("Set Redis error: %v", err) + return + } + + forgetUrl = config.GetSystemBaseUrl() + forgetPasswordUrlPrefix + forgetUrl + err = smtp.Send(user.Email, forgetPasswordTitle, fmt.Sprintf(forgetPasswordHTMLFormat, user.Username, forgetUrl, forgetUrl)) + if err != nil { + middleware.Error(ctx, middleware.CodeServeBusy, iris.StatusInternalServerError) + l.Error("Send SMTP Error: %v", err) + return + } + + middleware.Success(ctx) +} diff --git a/routers/api/v1/user/routers.go b/routers/api/v1/user/routers.go index 0c01102..17f0119 100644 --- a/routers/api/v1/user/routers.go +++ b/routers/api/v1/user/routers.go @@ -25,6 +25,8 @@ func InitUser(party router.Party) { party.Post("/register", registerHandler) party.Get("/profile", middleware.AuthCheck, profileHandler) party.Post("/password", middleware.AuthCheck, resetPasswordHandler) + party.Post("/forget", forgetSendHandler) + party.Put("/password", forgetPasswordHandler) party.Post("/email", middleware.AuthCheck, resetEmailHandler) party.Get("/verify/{code:string}", verifyHandler) party.Post("/verify", middleware.AuthCheck, verifySendHandler) diff --git a/routers/api/v1/user/verify.go b/routers/api/v1/user/verify.go index 0115683..e7adf64 100644 --- a/routers/api/v1/user/verify.go +++ b/routers/api/v1/user/verify.go @@ -30,7 +30,6 @@ func verifyHandler(ctx iris.Context) { } email := v.Val() - l.Debug("verify email: %s", email) user := models.Users{ Email: email, } diff --git a/routers/api/v1/user/verifySend.go b/routers/api/v1/user/verifySend.go index 63ebbab..9d3f1f9 100644 --- a/routers/api/v1/user/verifySend.go +++ b/routers/api/v1/user/verifySend.go @@ -73,7 +73,12 @@ func verifySendHandler(ctx iris.Context) { rdb := redis.RawDB verifyUrl := crypto.Hex(32) - rdb.Set(ctx, verifyRedisKeyPrefix+verifyUrl, user.Email, 15*time.Minute) + err = rdb.Set(ctx, verifyRedisKeyPrefix+verifyUrl, user.Email, 15*time.Minute).Err() + if err != nil { + middleware.Error(ctx, middleware.CodeServeBusy, iris.StatusInternalServerError) + l.Error("Set Redis Error: %v", err) + return + } verifyUrl = config.GetSystemBaseUrl() + verifyUrlPrefix + verifyUrl err = smtp.Send(user.Email, verifyTitle, fmt.Sprintf(verifyHTMLFormat, user.Username, verifyUrl, verifyUrl))