mirror of
https://github.com/fumiama/paper-manager.git
synced 2026-06-05 07:50:23 +08:00
finish dashboard/account
This commit is contained in:
354
backend/api.go
354
backend/api.go
@@ -1,10 +1,7 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fumiama/paper-manager/backend/global"
|
||||
"github.com/fumiama/paper-manager/backend/utils"
|
||||
@@ -25,357 +22,6 @@ func (h *apihandler) handle(w http.ResponseWriter, r *http.Request) {
|
||||
var apimap = make(map[string]*apihandler, 512)
|
||||
|
||||
func init() {
|
||||
apimap["/api/getLoginSalt"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
username := r.URL.Query().Get("username")
|
||||
if username == "" {
|
||||
writeresult(w, codeError, nil, "empty username", typeError)
|
||||
return
|
||||
}
|
||||
salt, err := getLoginSalt(username)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, salt, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/login"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type loginbody struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
var body loginbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
ret, err := login(body.Username, body.Password)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, ret, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/getUserInfo"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
ret, err := getUserInfo(token)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, ret, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/logout"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
err := logout(r.Header.Get("Authorization"))
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, nil, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/register"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type registerbody struct {
|
||||
Username string `json:"username"`
|
||||
Mobile string `json:"mobile"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
if r.Header.Get("Authorization") != "" {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body registerbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
ip := r.RemoteAddr
|
||||
i := strings.LastIndex(ip, ":")
|
||||
if i >= 0 {
|
||||
ip = ip[:i]
|
||||
}
|
||||
err = register(ip, body.Username, body.Mobile, body.Password)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "已上报, 请耐心等待通知"}, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/getUsersCount"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
n, err := getUsersCount(token)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, n, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/getUsersList"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
ret, err := getUsersList(token)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, &ret, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/isNameExist"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
name := r.URL.Query().Get("username")
|
||||
if name == "" {
|
||||
writeresult(w, codeError, nil, "empty username", typeError)
|
||||
return
|
||||
}
|
||||
yes, err := isNameExist(token, name)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, yes, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/setPassword"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type setpasswordbody struct {
|
||||
Token string `json:"token"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body setpasswordbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = setUserPassword(*user.ID, body.Token, body.Password)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "成功, 请重新登录"}, messageOk, typeSuccess)
|
||||
_ = logout(token)
|
||||
}}
|
||||
|
||||
apimap["/api/setContact"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type setcontactbody struct {
|
||||
Token string `json:"token"`
|
||||
Contact string `json:"contact"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body setcontactbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = setUserContact(*user.ID, body.Token, body.Contact)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
user.Cont = hideContact(body.Contact)
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "成功, 已将消息报告给课程组长"}, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/setUserInfo"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type setuserinfobody struct {
|
||||
Nick string `json:"nick"`
|
||||
Desc string `json:"desc"`
|
||||
Avtr string `json:"avtr"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body setuserinfobody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = setUserInfo(*user.ID, &body.Nick, &body.Desc, &body.Avtr)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
user.Nick = body.Nick
|
||||
user.Desc = body.Desc
|
||||
user.Avtr = body.Avtr
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "成功"}, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/setRole"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type setrolebody struct {
|
||||
ID int `json:"id"`
|
||||
Role global.UserRole `json:"role"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
if !user.IsSuper() {
|
||||
writeresult(w, codeError, nil, errNoSetRolePermission.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body setrolebody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
if body.ID == *user.ID {
|
||||
writeresult(w, codeError, nil, "cannot set self", typeError)
|
||||
return
|
||||
}
|
||||
err = setUserRole(body.ID, body.Role, user.Name)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, nil, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/disableUser"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type disableuserbody struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
if !user.IsSuper() {
|
||||
writeresult(w, codeError, nil, errNoSetRolePermission.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body disableuserbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
if body.ID == *user.ID {
|
||||
writeresult(w, codeError, nil, "cannot disbale self", typeError)
|
||||
return
|
||||
}
|
||||
err = global.UserDB.DisableUser(body.ID, user.Name)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, nil, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/resetPassword"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type resetpwdbody struct {
|
||||
Username string `json:"username"`
|
||||
Mobile string `json:"mobile"`
|
||||
}
|
||||
if r.Header.Get("Authorization") != "" {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body resetpwdbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
ip := r.RemoteAddr
|
||||
i := strings.LastIndex(ip, ":")
|
||||
if i >= 0 {
|
||||
ip = ip[:i]
|
||||
}
|
||||
err = resetPassword(ip, body.Username, body.Mobile)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "已上报, 请耐心等待通知"}, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/getMessageList"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
ret, err := getMessageList(token)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, ret, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/acceptMessage"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(r.URL.Query().Get("id"))
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = acceptMessage(r.Header.Get("Authorization"), id)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, "成功", messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/delMessage"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(r.URL.Query().Get("id"))
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = delMessage(r.Header.Get("Authorization"), id)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, "成功", messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/getAnnualVisits"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
crand "crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -147,3 +149,58 @@ func login(username, challenge string) (*loginResult, error) {
|
||||
Desc: user.Desc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func logout(token string) error {
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
return errInvalidToken
|
||||
}
|
||||
loginstatus.Delete(user.Name)
|
||||
usertokens.Delete(token)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
apimap["/api/getLoginSalt"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
username := r.URL.Query().Get("username")
|
||||
if username == "" {
|
||||
writeresult(w, codeError, nil, "empty username", typeError)
|
||||
return
|
||||
}
|
||||
salt, err := getLoginSalt(username)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, salt, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/login"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type loginbody struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
var body loginbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
ret, err := login(body.Username, body.Password)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, ret, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/logout"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
err := logout(r.Header.Get("Authorization"))
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, nil, messageOk, typeSuccess)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package backend
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/paper-manager/backend/global"
|
||||
@@ -107,3 +109,43 @@ func delMessage(token string, id int) error {
|
||||
}
|
||||
return global.UserDB.DelMessageByID(id)
|
||||
}
|
||||
|
||||
func init() {
|
||||
apimap["/api/getMessageList"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
ret, err := getMessageList(token)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, ret, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/acceptMessage"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(r.URL.Query().Get("id"))
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = acceptMessage(r.Header.Get("Authorization"), id)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, "成功", messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/delMessage"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(r.URL.Query().Get("id"))
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = delMessage(r.Header.Get("Authorization"), id)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, "成功", messageOk, typeSuccess)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/ttl"
|
||||
@@ -25,3 +28,38 @@ func register(ip, name, mobile, npwd string) error {
|
||||
registerlimit.Set(ip, true)
|
||||
return global.UserDB.NotifyRegister(ip, name, mobile, npwd)
|
||||
}
|
||||
|
||||
func init() {
|
||||
apimap["/api/register"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type registerbody struct {
|
||||
Username string `json:"username"`
|
||||
Mobile string `json:"mobile"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
if r.Header.Get("Authorization") != "" {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body registerbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
ip := r.RemoteAddr
|
||||
i := strings.LastIndex(ip, ":")
|
||||
if i >= 0 {
|
||||
ip = ip[:i]
|
||||
}
|
||||
err = register(ip, body.Username, body.Mobile, body.Password)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "已上报, 请耐心等待通知"}, messageOk, typeSuccess)
|
||||
}}
|
||||
}
|
||||
|
||||
287
backend/user.go
287
backend/user.go
@@ -3,7 +3,9 @@ package backend
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -74,16 +76,6 @@ func getUserInfo(token string) (*getUserInfoResult, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func logout(token string) error {
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
return errInvalidToken
|
||||
}
|
||||
loginstatus.Delete(user.Name)
|
||||
usertokens.Delete(token)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUsersCount(token string) (int, error) {
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
@@ -196,6 +188,25 @@ func setUserInfo(id int, nick, desc, avtr *string) error {
|
||||
return global.UserDB.UpdateUserInfo(id, user.Name, n, a, d)
|
||||
}
|
||||
|
||||
// setOthersInfo may change the arguments
|
||||
func setOthersInfo(id int, opname, nick, desc string) error {
|
||||
user, err := global.UserDB.GetUserByID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if nick == user.Nick {
|
||||
nick = ""
|
||||
} else if nick != "" {
|
||||
user.Nick = nick
|
||||
}
|
||||
if desc == user.Desc {
|
||||
desc = ""
|
||||
} else if desc != "" {
|
||||
user.Desc = desc
|
||||
}
|
||||
return global.UserDB.UpdateUserInfo(id, opname, nick, "", desc)
|
||||
}
|
||||
|
||||
func setUserRole(id int, role global.UserRole, opname string) error {
|
||||
if !role.IsVaild() {
|
||||
return errInvalidRole
|
||||
@@ -204,6 +215,9 @@ func setUserRole(id int, role global.UserRole, opname string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if role == user.Role {
|
||||
return nil
|
||||
}
|
||||
return global.UserDB.UpdateUserRole(*user.ID, role, opname)
|
||||
}
|
||||
|
||||
@@ -217,3 +231,256 @@ func resetPassword(ip, name, mobile string) error {
|
||||
registerlimit.Set(ip, true)
|
||||
return global.UserDB.NotifyResetPassword(ip, name, mobile)
|
||||
}
|
||||
|
||||
func init() {
|
||||
apimap["/api/getUserInfo"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
ret, err := getUserInfo(token)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, ret, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/getUsersCount"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
n, err := getUsersCount(token)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, n, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/getUsersList"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
ret, err := getUsersList(token)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, &ret, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/isNameExist"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
name := r.URL.Query().Get("username")
|
||||
if name == "" {
|
||||
writeresult(w, codeError, nil, "empty username", typeError)
|
||||
return
|
||||
}
|
||||
yes, err := isNameExist(token, name)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, yes, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/setPassword"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type setpasswordbody struct {
|
||||
Token string `json:"token"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body setpasswordbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = setUserPassword(*user.ID, body.Token, body.Password)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "成功, 请重新登录"}, messageOk, typeSuccess)
|
||||
_ = logout(token)
|
||||
}}
|
||||
|
||||
apimap["/api/setContact"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type setcontactbody struct {
|
||||
Token string `json:"token"`
|
||||
Contact string `json:"contact"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body setcontactbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
err = setUserContact(*user.ID, body.Token, body.Contact)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
user.Cont = hideContact(body.Contact)
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "成功, 已将消息报告给课程组长"}, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/setUserInfo"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type setuserinfobody struct {
|
||||
ID int `json:"id"`
|
||||
Nick string `json:"nick"`
|
||||
Desc string `json:"desc"`
|
||||
Avtr string `json:"avtr"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body setuserinfobody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
if body.ID != 0 {
|
||||
if !user.IsSuper() {
|
||||
writeresult(w, codeError, nil, "no permission to set others' info", typeError)
|
||||
return
|
||||
}
|
||||
err = setOthersInfo(body.ID, user.Name, body.Nick, body.Desc)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "成功"}, messageOk, typeSuccess)
|
||||
return
|
||||
}
|
||||
err = setUserInfo(*user.ID, &body.Nick, &body.Desc, &body.Avtr)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
user.Nick = body.Nick
|
||||
user.Desc = body.Desc
|
||||
user.Avtr = body.Avtr
|
||||
writeresult(w, codeSuccess, &message{M: "成功"}, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/setRole"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type setrolebody struct {
|
||||
ID int `json:"id"`
|
||||
Role global.UserRole `json:"role"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
if !user.IsSuper() {
|
||||
writeresult(w, codeError, nil, errNoSetRolePermission.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body setrolebody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
if body.ID == *user.ID {
|
||||
writeresult(w, codeError, nil, "cannot set self", typeError)
|
||||
return
|
||||
}
|
||||
err = setUserRole(body.ID, body.Role, user.Name)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, nil, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/disableUser"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type disableuserbody struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
token := r.Header.Get("Authorization")
|
||||
user := usertokens.Get(token)
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
if !user.IsSuper() {
|
||||
writeresult(w, codeError, nil, errNoSetRolePermission.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body disableuserbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
if body.ID == *user.ID {
|
||||
writeresult(w, codeError, nil, "cannot disbale self", typeError)
|
||||
return
|
||||
}
|
||||
err = global.UserDB.DisableUser(body.ID, user.Name)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
writeresult(w, codeSuccess, nil, messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/resetPassword"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
type resetpwdbody struct {
|
||||
Username string `json:"username"`
|
||||
Mobile string `json:"mobile"`
|
||||
}
|
||||
if r.Header.Get("Authorization") != "" {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
var body resetpwdbody
|
||||
defer r.Body.Close()
|
||||
err := json.NewDecoder(r.Body).Decode(&body)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
ip := r.RemoteAddr
|
||||
i := strings.LastIndex(ip, ":")
|
||||
if i >= 0 {
|
||||
ip = ip[:i]
|
||||
}
|
||||
err = resetPassword(ip, body.Username, body.Mobile)
|
||||
if err != nil {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
type message struct {
|
||||
M string `json:"msg"`
|
||||
}
|
||||
writeresult(w, codeSuccess, &message{M: "已上报, 请耐心等待通知"}, messageOk, typeSuccess)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,15 @@ export interface SetUserInfoParams {
|
||||
avtr: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Set Others' Info interface parameters
|
||||
*/
|
||||
export interface SetOthersInfoParams {
|
||||
id: number
|
||||
nick: string
|
||||
desc: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Register interface parameters
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
SetPasswordParams,
|
||||
SetContactParams,
|
||||
SetUserInfoParams,
|
||||
SetOthersInfoParams,
|
||||
RegisterParams,
|
||||
ResetPasswordResultModel,
|
||||
RegisterResultModel,
|
||||
@@ -108,6 +109,21 @@ export function setUserInfoApi(params: SetUserInfoParams, mode: ErrorMessageMode
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: set userinfo api, borrowing the ResetPasswordResultModel as they're the same
|
||||
*/
|
||||
export function setOthersInfoApi(params: SetOthersInfoParams, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.post<ResetPasswordResultModel>(
|
||||
{
|
||||
url: Api.SetUserInfo,
|
||||
params,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: register api
|
||||
*/
|
||||
@@ -166,7 +182,7 @@ export function doLogout() {
|
||||
}
|
||||
|
||||
export const setRole = (id: number, role: number) =>
|
||||
defHttp.post({ url: Api.SetRole, params: { id, role } }, { errorMessageMode: 'none' })
|
||||
defHttp.post({ url: Api.SetRole, params: { id, role } })
|
||||
|
||||
export const disableUser = (id: number, stat: boolean) =>
|
||||
defHttp.post({ url: Api.DisableUser, params: { id, stat } }, { errorMessageMode: 'none' })
|
||||
|
||||
@@ -1,65 +1,26 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="编辑账号" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, unref } from 'vue'
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal'
|
||||
import { BasicForm, useForm } from '/@/components/Form/index'
|
||||
import { isNameExist } from '/@/api/sys/user'
|
||||
import { setOthersInfoApi, setRole } from '/@/api/sys/user'
|
||||
import { useUserStore } from '/@/store/modules/user'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AccountModal',
|
||||
components: { BasicModal, BasicForm },
|
||||
emits: ['success', 'register'],
|
||||
setup(_, { emit }) {
|
||||
const isUpdate = ref(true)
|
||||
const rowId = ref('')
|
||||
|
||||
const [registerForm, { setFieldsValue, updateSchema, resetFields, validate }] = useForm({
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
|
||||
labelWidth: 100,
|
||||
baseColProps: { span: 24 },
|
||||
schemas: [
|
||||
{
|
||||
field: 'name',
|
||||
label: '用户名',
|
||||
component: 'Input',
|
||||
ifShow: () => {
|
||||
return !unref(isUpdate)
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入用户名',
|
||||
},
|
||||
{
|
||||
validator(_, value) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (unref(isUpdate)) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
isNameExist(value)
|
||||
.then((v) => {
|
||||
if (!v) resolve()
|
||||
else reject('用户名已存在')
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err.message || '验证失败')
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'pwd',
|
||||
label: '密码',
|
||||
component: 'InputPassword',
|
||||
required: true,
|
||||
ifShow: false,
|
||||
},
|
||||
{
|
||||
label: '角色',
|
||||
field: 'role',
|
||||
@@ -107,39 +68,31 @@
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
resetFields()
|
||||
setModalProps({ confirmLoading: false })
|
||||
isUpdate.value = !!data?.isUpdate
|
||||
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.record.id
|
||||
setFieldsValue({
|
||||
...data.record,
|
||||
})
|
||||
}
|
||||
|
||||
updateSchema([
|
||||
{
|
||||
field: 'pwd',
|
||||
show: !unref(isUpdate),
|
||||
},
|
||||
])
|
||||
rowId.value = data.record.id
|
||||
setFieldsValue({
|
||||
...data.record,
|
||||
})
|
||||
})
|
||||
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增账号' : '编辑账号'))
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate()
|
||||
setModalProps({ confirmLoading: true })
|
||||
// TODO custom api
|
||||
console.log(values)
|
||||
closeModal()
|
||||
emit('success', { isUpdate: unref(isUpdate), values: { ...values, id: rowId.value } })
|
||||
await setOthersInfoApi({ id: Number(rowId.value), nick: values.nick, desc: values.desc })
|
||||
if (useUserStore().getUserInfo.userId != Number(rowId.value))
|
||||
await setRole(
|
||||
Number(rowId.value),
|
||||
{ 课程组长: 1, 归档代理: 2, 课程组员: 3 }[values.role],
|
||||
)
|
||||
emit('success', { values: { ...values, id: rowId.value } })
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false })
|
||||
}
|
||||
}
|
||||
|
||||
return { registerModal, registerForm, getTitle, handleSubmit }
|
||||
return { registerModal, registerForm, handleSubmit }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BasicColumn } from '/@/components/Table'
|
||||
import { FormSchema } from '/@/components/Table'
|
||||
import { h } from 'vue'
|
||||
import { Switch } from 'ant-design-vue'
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
@@ -67,18 +66,3 @@ export const columns: BasicColumn[] = [
|
||||
dataIndex: 'desc',
|
||||
},
|
||||
]
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: '用户名',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
{
|
||||
field: 'nick',
|
||||
label: '昵称',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<BasicTable @register="registerTable" :searchInfo="searchInfo">
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleCreate">新增账号</a-button>
|
||||
</template>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction
|
||||
@@ -13,17 +10,6 @@
|
||||
tooltip: '编辑用户资料',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
color: 'error',
|
||||
tooltip: '删除此账号',
|
||||
popConfirm: {
|
||||
title:
|
||||
'为确保安全, 删除账号仅仅是禁止了登录, 必要情况下用户仍然可以通过忘记密码的方式找回账号',
|
||||
placement: 'left',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
@@ -33,7 +19,7 @@
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive } from 'vue'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table'
|
||||
import { getUsersList } from '/@/api/sys/user'
|
||||
@@ -42,81 +28,46 @@
|
||||
import { useModal } from '/@/components/Modal'
|
||||
import AccountModal from './AccountModal.vue'
|
||||
|
||||
import { columns, searchFormSchema } from './account.data'
|
||||
import { columns } from './account.data'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AccountManagement',
|
||||
components: { BasicTable, PageWrapper, AccountModal, TableAction },
|
||||
setup() {
|
||||
const [registerModal, { openModal }] = useModal()
|
||||
const searchInfo = reactive<Recordable>({})
|
||||
const [registerTable, { reload, updateTableDataRecord }] = useTable({
|
||||
const [registerTable, { updateTableDataRecord }] = useTable({
|
||||
title: '账号列表',
|
||||
api: getUsersList,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
formConfig: {
|
||||
labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
autoSubmitOnEnter: true,
|
||||
},
|
||||
useSearchForm: true,
|
||||
useSearchForm: false,
|
||||
showTableSetting: true,
|
||||
bordered: true,
|
||||
handleSearchInfoFn(info) {
|
||||
console.log('handleSearchInfoFn', info)
|
||||
return info
|
||||
},
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
width: 80,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
// slots: { customRender: 'action' },
|
||||
},
|
||||
})
|
||||
|
||||
function handleCreate() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
})
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
console.log(record)
|
||||
// console.log(record)
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(record: Recordable) {
|
||||
console.log(record)
|
||||
}
|
||||
|
||||
function handleSuccess({ isUpdate, values }) {
|
||||
if (isUpdate) {
|
||||
// 演示不刷新表格直接更新内部数据。
|
||||
// 注意:updateTableDataRecord要求表格的rowKey属性为string并且存在于每一行的record的keys中
|
||||
updateTableDataRecord(values.id, values)
|
||||
} else {
|
||||
reload()
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelect(deptId = '') {
|
||||
searchInfo.deptId = deptId
|
||||
reload()
|
||||
function handleSuccess({ values }) {
|
||||
// 演示不刷新表格直接更新内部数据。
|
||||
// 注意:updateTableDataRecord要求表格的rowKey属性为string并且存在于每一行的record的keys中
|
||||
updateTableDataRecord(values.id, values)
|
||||
}
|
||||
|
||||
return {
|
||||
registerTable,
|
||||
registerModal,
|
||||
handleCreate,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleSuccess,
|
||||
handleSelect,
|
||||
searchInfo,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user