From 5084bcb80e80f4976adeb5b07eb005501322d20d Mon Sep 17 00:00:00 2001 From: sunface Date: Thu, 18 Mar 2021 08:44:07 +0800 Subject: [PATCH] update --- pages/[username]/index.tsx | 4 +- pages/admin/users.tsx | 160 ++++++++++++++++++++++++++ pages/settings/orgs.tsx | 40 +------ pages/settings/profile.tsx | 52 +-------- server/internal/admin/admin.go | 27 +++++ server/internal/api/admin.go | 53 +++++++++ server/internal/api/posts.go | 18 +++ server/internal/api/user.go | 18 ++- server/internal/cache/cache.go | 4 +- server/internal/interaction/follow.go | 2 +- server/internal/server.go | 11 +- server/internal/storage/init.go | 4 +- server/internal/story/posts.go | 34 ++++++ server/internal/user/user.go | 54 +++++++++ src/components/story/stories.tsx | 5 +- src/components/story/story-author.tsx | 5 +- src/components/story/story-card.tsx | 4 +- src/data/links.tsx | 6 + src/utils/user.ts | 86 +++++++++++++- 19 files changed, 484 insertions(+), 103 deletions(-) create mode 100644 pages/admin/users.tsx create mode 100644 server/internal/admin/admin.go create mode 100644 server/internal/api/admin.go diff --git a/pages/[username]/index.tsx b/pages/[username]/index.tsx index 1f65dcb0..6f05cb39 100644 --- a/pages/[username]/index.tsx +++ b/pages/[username]/index.tsx @@ -51,7 +51,7 @@ const UserPage = () => { getTags(res.data.id) getNavbars(res.data.id) - const res1 = await requestApi.get(`/user/posts/${res.data.id}`) + const res1 = res.data.type === IDType.User ? await requestApi.get(`/user/posts/${res.data.id}`) : await requestApi.get(`/story/posts/org/${res.data.id}?type=0`) setPosts(res1.data) setRawPosts(res1.data) } @@ -251,7 +251,7 @@ const UserPage = () => { : - + } diff --git a/pages/admin/users.tsx b/pages/admin/users.tsx new file mode 100644 index 00000000..3af75651 --- /dev/null +++ b/pages/admin/users.tsx @@ -0,0 +1,160 @@ +import { Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast, Table, Thead, Tr, Th, Tbody, Td, IconButton, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, useDisclosure, FormControl, FormLabel, Input, FormErrorMessage, Select} from "@chakra-ui/react" +import Card from "components/card" +import Sidebar from "layouts/sidebar/sidebar" +import React, { useEffect, useState } from "react" +import { adminLinks } from "src/data/links" +import { requestApi } from "utils/axios/request" +import TagCard from "components/tags/tag-card" +import { useRouter } from "next/router" +import Link from "next/link" +import { ReserveUrls } from "src/data/reserve-urls" +import { Tag } from "src/types/tag" +import { route } from "next/dist/next-server/server/router" +import PageContainer1 from "layouts/page-container1" +import Empty from "components/empty" +import { User } from "src/types/user" +import moment from 'moment' +import { getSvgIcon } from "components/svg-icon" +import { Field, Form, Formik } from "formik" +import { validateEmail, validateNickname, validateUsername } from "utils/user" +import { Role } from "src/types/role" + +const PostsPage = () => { + const { isOpen, onOpen, onClose } = useDisclosure() + const [currentUser,setCurrentUser]:[User,any] = useState(null) + const [users, setUsers]: [User[], any] = useState([]) + const router = useRouter() + const toast = useToast() + const getUsers = async () => { + const res = await requestApi.get(`/admin/user/all`) + setUsers(res.data) + } + + useEffect(() => { + getUsers() + }, []) + + const onEditUser = user => { + if (!user) { + // add user + setCurrentUser({role:Role.NORMAL}) + } else { + // edit user + setCurrentUser(user) + } + onOpen() + } + + const submitUser = async values => { + await requestApi.post("/admin/user",values) + getUsers() + onClose() + } + + return ( + <> + + + + + + 用户列表({users.length}) + + + + + + + + + + + + + + { + users.map((user, i) => + + + + + + ) + } + + +
用户名邮箱角色加入时间
{user.username}{user.email}{user.role}{moment(user.created).fromNow()} + onEditUser(user)} /> + {/* onDeleteUser(user)} /> */} +
+
+
+
+ + + + {currentUser && + {currentUser.id ? '编辑用户' : '新建用户'} + + + {(props) => ( +
+ + + {({ field, form }) => ( + + Username + + {form.errors.username} + + )} + + + {({ field, form }) => ( + + 邮箱地址 + + {form.errors.email} + + + )} + + + {({ field, form }) => ( + + Role + + {form.errors.role} + + )} + + + + + + +
+ )} +
+
+
} +
+ + ) +} +export default PostsPage + diff --git a/pages/settings/orgs.tsx b/pages/settings/orgs.tsx index b80a6f53..8518cbb7 100644 --- a/pages/settings/orgs.tsx +++ b/pages/settings/orgs.tsx @@ -1,4 +1,4 @@ -import { Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast, FormControl, FormLabel, FormHelperText, Input, FormErrorMessage, HStack, Wrap, useMediaQuery, Avatar, Textarea, Table, Thead, Tr, Th, Tbody, Td, IconButton, useDisclosure, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalFooter, Select, NumberInput, NumberInputField, NumberInputStepper, NumberIncrementStepper, NumberDecrementStepper, useColorModeValue, StackDivider } from "@chakra-ui/react" +import { Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast, FormControl, FormLabel, FormHelperText, Input, FormErrorMessage, HStack, useDisclosure, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, useColorModeValue, StackDivider } from "@chakra-ui/react" import Card from "components/card" import PageContainer from "layouts/page-container" import Sidebar from "layouts/sidebar/sidebar" @@ -8,7 +8,7 @@ import { requestApi } from "utils/axios/request" import { Org } from "src/types/org" import { Field, Form, Formik } from "formik" import { config } from "configs/config" -import { isUsernameChar, usernameInvalidTips } from "utils/user" +import { isUsernameChar, usernameInvalidTips, validateNickname, validateUsername } from "utils/user" import { isAdmin } from "utils/role" import userCustomTheme from "theme/user-custom" import { useRouter } from "next/router" @@ -41,41 +41,7 @@ const UserOrgsPage = () => { onOpen() } - const validateUsername = async value => { - let error - if (!value?.trim()) { - return "不能为空" - } - - if (value?.length > config.user.usernameMaxLen) { - return `长度不能超过${config.user.usernameMaxLen}` - } - - for (const c of value) { - if (!isUsernameChar(c)) { - return usernameInvalidTips - } - } - - const res = await requestApi.get(`/username/exist/${value}`) - if (res.data) { - return `The name '${value}' is already taken.` - } - return error - } - - function validateNickname(value) { - let error - if (!value?.trim()) { - error = "不能为空" - } - - if (value?.length > config.user.usernameMaxLen) { - error = `长度不能超过${config.user.usernameMaxLen}` - } - - return error - } + return ( diff --git a/pages/settings/profile.tsx b/pages/settings/profile.tsx index 28946a74..2fa9bd45 100644 --- a/pages/settings/profile.tsx +++ b/pages/settings/profile.tsx @@ -10,7 +10,8 @@ import { useRouter } from "next/router" import { Field, Form, Formik } from "formik" import { config } from "configs/config" import Tags from "components/tags/tags" -var validator = require('validator'); +import { validateNickname ,validateEmail,validateUrl} from "utils/user" + const UserProfilePage = () => { const [user, setUser] = useState(null) @@ -33,57 +34,8 @@ const UserProfilePage = () => { }) } - function validateNickname(value) { - let error - if (!value?.trim()) { - error = "昵称不能为空" - } - - if (value?.length > config.user.nicknameMaxLen) { - error = `长度不能超过${config.user.nicknameMaxLen}` - } - - return error - } - - function validateEmail(value) { - let email = value?.trim() - let error - if (email?.length > config.user.usernameMaxLen) { - error = `长度不能超过${config.user.usernameMaxLen}` - return error - } - if (email) { - if (!validator.isEmail(email)) { - error = "Email格式不合法" - return error - } - } - return error - } - - - function validateUrl(value, canBeEmpty = true) { - let url = value?.trim() - let error - if (!canBeEmpty) { - if (!url) { - error = "url不能为空" - return error - } - } - - if (url) { - if (!validator.isURL(value)) { - error = "URL格式不合法" - return error - } - } - - return error - } function validateLen(value) { let error diff --git a/server/internal/admin/admin.go b/server/internal/admin/admin.go new file mode 100644 index 00000000..f218f2ba --- /dev/null +++ b/server/internal/admin/admin.go @@ -0,0 +1,27 @@ +package admin + +import ( + "net/http" + + "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" + "github.com/imdotdev/im.dev/server/pkg/db" + "github.com/imdotdev/im.dev/server/pkg/e" + "github.com/imdotdev/im.dev/server/pkg/models" +) + +func GetUsers() ([]*models.User, *e.Error) { + users := make([]*models.User, 0) + rows, err := db.Conn.Query("SELECT id,username,email,role,created FROM user WHERE type=?", models.IDTypeUser) + if err != nil { + logger.Warn("get users error", "error", err) + return nil, e.New(http.StatusInternalServerError, e.Internal) + } + + for rows.Next() { + user := &models.User{} + rows.Scan(&user.ID, &user.Username, &user.Email, &user.Role, &user.Created) + users = append(users, user) + } + + return users, nil +} diff --git a/server/internal/api/admin.go b/server/internal/api/admin.go new file mode 100644 index 00000000..6cf4953d --- /dev/null +++ b/server/internal/api/admin.go @@ -0,0 +1,53 @@ +package api + +import ( + "net/http" + + "github.com/asaskevich/govalidator" + "github.com/gin-gonic/gin" + "github.com/imdotdev/im.dev/server/internal/admin" + "github.com/imdotdev/im.dev/server/internal/user" + "github.com/imdotdev/im.dev/server/pkg/common" + "github.com/imdotdev/im.dev/server/pkg/e" + "github.com/imdotdev/im.dev/server/pkg/models" +) + +func AdminSubmitUser(c *gin.Context) { + currentUser := user.CurrentUser(c) + if !currentUser.Role.IsAdmin() { + c.JSON(http.StatusForbidden, common.RespError(e.NoPermission)) + return + } + + u := &models.User{} + c.Bind(&u) + + if u.Username == "" || u.Email == "" || !govalidator.IsEmail(u.Email) || !u.Role.IsValid() { + c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid)) + return + } + + err := user.SubmitUser(u) + if err != nil { + c.JSON(err.Status, common.RespError(err.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(nil)) +} + +func AdminGetUsers(c *gin.Context) { + currentUser := user.CurrentUser(c) + if !currentUser.Role.IsAdmin() { + c.JSON(http.StatusForbidden, common.RespError(e.NoPermission)) + return + } + + users, err := admin.GetUsers() + if err != nil { + c.JSON(err.Status, common.RespError(err.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(users)) +} diff --git a/server/internal/api/posts.go b/server/internal/api/posts.go index d3c4b0a3..5b1fe841 100644 --- a/server/internal/api/posts.go +++ b/server/internal/api/posts.go @@ -27,6 +27,24 @@ func GetEditorPosts(c *gin.Context) { c.JSON(http.StatusOK, common.RespSuccess(ars)) } +func GetOrgPosts(c *gin.Context) { + orgID := c.Param("id") + tp := c.Query("type") + if tp != models.IDTypeUndefined && !models.ValidStoryIDType(tp) { + c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid)) + return + } + + user := user.CurrentUser(c) + ars, err := story.OrgPosts(tp, user, orgID) + if err != nil { + c.JSON(err.Status, common.RespError(err.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(ars)) +} + func GetEditorDrafts(c *gin.Context) { user := user.CurrentUser(c) ars, err := story.UserDrafts(nil, user.ID) diff --git a/server/internal/api/user.go b/server/internal/api/user.go index a255458d..fffe2589 100644 --- a/server/internal/api/user.go +++ b/server/internal/api/user.go @@ -143,7 +143,7 @@ func DeleteUserNavbar(c *gin.Context) { c.JSON(http.StatusOK, common.RespSuccess(nil)) } -func NameExist(c *gin.Context) { +func UserNameExist(c *gin.Context) { name := c.Param("name") if strings.TrimSpace(name) == "" { c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid)) @@ -158,3 +158,19 @@ func NameExist(c *gin.Context) { c.JSON(http.StatusOK, common.RespSuccess(exist)) } + +func UserEmailExist(c *gin.Context) { + email := c.Param("email") + if strings.TrimSpace(email) == "" { + c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid)) + return + } + + exist, err := user.EmailExist(email) + if err != nil { + c.JSON(err.Status, common.RespError(err.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(exist)) +} diff --git a/server/internal/cache/cache.go b/server/internal/cache/cache.go index 9f8a704e..63e5b7f6 100644 --- a/server/internal/cache/cache.go +++ b/server/internal/cache/cache.go @@ -16,7 +16,7 @@ func Init() { time.Sleep(2 * time.Second) for { // load users - rows, err := db.Conn.Query(`SELECT id,username,role,nickname,avatar,last_seen_at,created FROM user`) + rows, err := db.Conn.Query(`SELECT id,type,username,role,nickname,avatar,last_seen_at,created FROM user`) if err != nil { logger.Error("load users error", "error", err) time.Sleep(60 * time.Second) @@ -27,7 +27,7 @@ func Init() { usersMap := make(map[string]*models.User) for rows.Next() { user := &models.User{} - err := rows.Scan(&user.ID, &user.Username, &user.Role, &user.Nickname, &user.Avatar, &user.LastSeenAt, &user.Created) + err := rows.Scan(&user.ID, &user.Type, &user.Username, &user.Role, &user.Nickname, &user.Avatar, &user.LastSeenAt, &user.Created) if err != nil { logger.Warn("scan user error", "error", err) continue diff --git a/server/internal/interaction/follow.go b/server/internal/interaction/follow.go index 05a1ea42..c1a47902 100644 --- a/server/internal/interaction/follow.go +++ b/server/internal/interaction/follow.go @@ -135,7 +135,7 @@ func GetFollowers(targetID, targetType string) ([]*models.User, *e.Error) { u, ok := models.UsersMapCache[id] if ok { users = append(users, u) - u.Followed = GetFollowed(u.ID, targetID) + u.Followed = GetFollowed(targetID, u.ID) u.Follows = GetFollows(u.ID) } } diff --git a/server/internal/server.go b/server/internal/server.go index 3ed124ca..86ec4767 100644 --- a/server/internal/server.go +++ b/server/internal/server.go @@ -51,6 +51,7 @@ func (s *Server) Start() error { r.POST("/story/comment", IsLogin(), api.SubmitComment) r.DELETE("/story/comment/:id", IsLogin(), api.DeleteStoryComment) r.GET("/story/posts/editor", IsLogin(), api.GetEditorPosts) + r.GET("/story/posts/org/:id", IsLogin(), api.GetOrgPosts) r.GET("/story/posts/drafts", IsLogin(), api.GetEditorDrafts) r.GET("/story/posts/home/:filter", api.GetHomePosts) r.POST("/story", IsLogin(), api.SubmitStory) @@ -87,6 +88,9 @@ func (s *Server) Start() error { r.POST("/user/navbar", IsLogin(), api.SubmitUserNavbar) r.GET("/user/navbars/:userID", api.GetUserNavbars) r.DELETE("/user/navbar/:id", IsLogin(), api.DeleteUserNavbar) + r.GET("/user/name/exist/:name", api.UserNameExist) + r.GET("/user/email/exist/:email", api.UserEmailExist) + // interaction apis r.POST("/interaction/like/:id", IsLogin(), api.Like) r.POST("/interaction/follow/:id", IsLogin(), api.Follow) @@ -104,12 +108,17 @@ func (s *Server) Start() error { r.POST("/org/update", IsLogin(), api.UpdateOrg) r.GET("/org/byUserID/:userID", api.GetOrgByUserID) r.GET("/org/members/:id", api.GetOrgMembers) + + // admin apis + r.POST("/admin/user", IsLogin(), api.AdminSubmitUser) + r.GET("/admin/user/all", IsLogin(), api.AdminGetUsers) + // other apis r.GET("/config", GetConfig) r.GET("/navbars", GetNavbars) r.POST("/navbar", IsLogin(), SubmitNavbar) r.DELETE("/navbar/:id", IsLogin(), DeleteNavbar) - r.GET("/username/exist/:name", api.NameExist) + err := router.Run(config.Data.Server.Addr) if err != nil { logger.Crit("start backend server error", "error", err) diff --git a/server/internal/storage/init.go b/server/internal/storage/init.go index 2ff86ffc..e30c447b 100644 --- a/server/internal/storage/init.go +++ b/server/internal/storage/init.go @@ -41,9 +41,9 @@ func Init() error { } var navbars = []*models.Navbar{ - &models.Navbar{Label: "主页", Value: "/", Weight: 0}, + &models.Navbar{Label: "主页", Value: "/", Weight: 2}, &models.Navbar{Label: "标签", Value: "/tags", Weight: 1}, - &models.Navbar{Label: "Search", Value: "/search/posts", Weight: 2}, + &models.Navbar{Label: "Search", Value: "/search/posts", Weight: 0}, } func initTables() error { diff --git a/server/internal/story/posts.go b/server/internal/story/posts.go index e411c799..16e22818 100644 --- a/server/internal/story/posts.go +++ b/server/internal/story/posts.go @@ -64,6 +64,40 @@ func UserPosts(tp string, user *models.User, uid string) (models.Stories, *e.Err return newPosts, nil } +func OrgPosts(tp string, user *models.User, orgID string) (models.Stories, *e.Error) { + var rows *sql.Rows + var err error + if tp == models.IDTypeUndefined { + rows, err = db.Conn.Query(PostQueryPrefix+"where owner=? and status=?", orgID, models.StatusPublished) + } else { + rows, err = db.Conn.Query(PostQueryPrefix+"where owner=? and type=? and status=?", orgID, tp, models.StatusPublished) + } + + if err != nil && err != sql.ErrNoRows { + logger.Warn("get user posts error", "error", err) + return nil, e.New(http.StatusInternalServerError, e.Internal) + } + + posts := GetPosts(user, rows) + + sort.Sort(posts) + + pinned := make([]*models.Story, 0) + unpinned := make([]*models.Story, 0) + + for _, post := range posts { + post.Pinned = GetPinned(post.ID, user.ID) + if post.Pinned { + pinned = append(pinned, post) + } else { + unpinned = append(unpinned, post) + } + } + + newPosts := append(pinned, unpinned...) + return newPosts, nil +} + func UserDrafts(user *models.User, uid string) (models.Stories, *e.Error) { rows, err := db.Conn.Query(PostQueryPrefix+"where creator=? and status=?", uid, models.StatusDraft) if err != nil && err != sql.ErrNoRows { diff --git a/server/internal/user/user.go b/server/internal/user/user.go index f3305a36..457eb979 100644 --- a/server/internal/user/user.go +++ b/server/internal/user/user.go @@ -12,11 +12,16 @@ import ( "github.com/imdotdev/im.dev/server/pkg/db" "github.com/imdotdev/im.dev/server/pkg/e" "github.com/imdotdev/im.dev/server/pkg/models" + "github.com/imdotdev/im.dev/server/pkg/utils" ) func GetUsers(q string) ([]*models.User, *e.Error) { users := make([]*models.User, 0) for _, u := range models.UsersCache { + if u.Type != models.IDTypeUser { + continue + } + if strings.HasPrefix(strings.ToLower(u.Nickname), strings.ToLower(q)) { users = append(users, u) continue @@ -142,3 +147,52 @@ func NameExist(name string) (bool, *e.Error) { return true, nil } + +func EmailExist(email string) (bool, *e.Error) { + var ne string + err := db.Conn.QueryRow("SELECT email FROM user WHERE email=?", email).Scan(&ne) + if err != nil && err != sql.ErrNoRows { + logger.Warn("check email exist error", "error", err) + return false, e.New(http.StatusInternalServerError, e.Internal) + } + + if err == sql.ErrNoRows { + return false, nil + } + + return true, nil +} + +func SubmitUser(user *models.User) *e.Error { + if user.Nickname == "" { + user.Nickname = "New user" + } + + var err error + now := time.Now() + if user.ID == "" { + // create user + emailExist, err0 := EmailExist(user.Email) + if err0 != nil { + return e.New(err0.Status, err0.Message) + } + + if emailExist { + return e.New(http.StatusConflict, "邮箱地址已存在") + } + + user.ID = utils.GenID(models.IDTypeUser) + _, err = db.Conn.Exec("INSERT INTO user (id,type,email,username,nickname,role,created,updated) VALUES (?,?,?,?,?,?,?,?)", + user.ID, models.IDTypeUser, user.Email, user.Username, user.Nickname, user.Role, now, now) + } else { + // update user + _, err = db.Conn.Exec("UPDATE user SET role=?,updated=? WHERE id=?", user.Role, now, user.ID) + } + + if err != nil { + logger.Warn("submit user error", "error", err) + return e.New(http.StatusInternalServerError, e.Internal) + } + + return nil +} diff --git a/src/components/story/stories.tsx b/src/components/story/stories.tsx index 125a4297..ba3b24ec 100644 --- a/src/components/story/stories.tsx +++ b/src/components/story/stories.tsx @@ -12,11 +12,12 @@ interface Props { showPinned?: boolean type?: string highlight?: string + showOrg?:boolean } export const Stroies = (props: Props) => { - const { stories,card=StoryCard,showFooter=true,type="classic",showPinned = false} = props + const { stories,card=StoryCard,showFooter=true,type="classic",showPinned = false,showOrg=true} = props const borderColor = useColorModeValue(userCustomTheme.borderColor.light, userCustomTheme.borderColor.dark) const Card = card const showBorder = i => { @@ -35,7 +36,7 @@ export const Stroies = (props: Props) => { {stories.map((story,i) => - + )} {showFooter &&
没有更多文章了
} diff --git a/src/components/story/story-author.tsx b/src/components/story/story-author.tsx index 7976516f..72f21b55 100644 --- a/src/components/story/story-author.tsx +++ b/src/components/story/story-author.tsx @@ -13,15 +13,16 @@ type Props = PropsOf & { size?: 'lg' | 'md' story : Story showFooter?: boolean + showOrg?: boolean } -export const StoryAuthor= ({story,showFooter=true,size='lg'}:Props) =>{ +export const StoryAuthor= ({story,showFooter=true,size='lg',showOrg=true}:Props) =>{ const router = useRouter() return ( router.push(`/${story.creator.username}`)} cursor="pointer"/> - {story.ownerId ? + {(showOrg && story.ownerId!=='') ? {story.creator.nickname} for diff --git a/src/components/story/story-card.tsx b/src/components/story/story-card.tsx index 6d242de0..4d89d5d4 100644 --- a/src/components/story/story-card.tsx +++ b/src/components/story/story-card.tsx @@ -16,6 +16,7 @@ interface Props { story: Story type?: string highlight?: string + showOrg?: boolean } @@ -24,10 +25,9 @@ export const StoryCard = (props: Props) => { const [isLargeScreen] = useMediaQuery("(min-width: 768px)") const Layout = isLargeScreen ? HStack : VStack - return ( - + diff --git a/src/data/links.tsx b/src/data/links.tsx index 20baaaef..aa0ff539 100644 --- a/src/data/links.tsx +++ b/src/data/links.tsx @@ -70,6 +70,12 @@ export const adminLinks: Route[] = [{ icon: getSvgIcon("navbar"), disabled: false }, +{ + title: '用户管理', + path: `${ReserveUrls.Admin}/users`, + icon: getSvgIcon("user"), + disabled: false +}, ] diff --git a/src/utils/user.ts b/src/utils/user.ts index 13badcc0..2686049d 100644 --- a/src/utils/user.ts +++ b/src/utils/user.ts @@ -1,4 +1,8 @@ +import { config } from 'configs/config' import {User} from 'src/types/user' +import { requestApi } from './axios/request' +var validator = require('validator'); + export function getUserName(user:User) { return user.nickname === "" ? user.username : user.nickname } @@ -11,4 +15,84 @@ export function isUsernameChar(c) { return false } -export const usernameInvalidTips = "May only contain alphanumeric characters or single hyphens, and cannot begin or end with a hyphen." \ No newline at end of file +export const usernameInvalidTips = "May only contain alphanumeric characters or single hyphens, and cannot begin or end with a hyphen." + + +export const validateUsername = async value => { + let error + if (!value?.trim()) { + return "不能为空" + } + + if (value?.length > config.user.usernameMaxLen) { + return `长度不能超过${config.user.usernameMaxLen}` + } + + for (const c of value) { + if (!isUsernameChar(c)) { + return usernameInvalidTips + } + } + + const res = await requestApi.get(`/user/name/exist/${value}`) + if (res.data) { + return `The name '${value}' is already taken.` + } + return error +} + +export function validateNickname(value) { + let error + if (!value?.trim()) { + error = "不能为空" + } + + if (value?.length > config.user.usernameMaxLen) { + error = `长度不能超过${config.user.usernameMaxLen}` + } + + return error +} + +export async function validateEmail(value) { + let email = value?.trim() + if (!email) { + return "邮箱不能为空" + } + + if (email?.length > config.user.usernameMaxLen) { + return `长度不能超过${config.user.usernameMaxLen}` + } + + if (email) { + if (!validator.isEmail(email)) { + return "Email格式不合法" + } + } + + const res = await requestApi.get(`/user/email/exist/${value}`) + if (res.data) { + return `The email '${value}' is already taken.` + } +} + + +export function validateUrl(value, canBeEmpty = true) { + let url = value?.trim() + let error + if (!canBeEmpty) { + if (!url) { + error = "url不能为空" + return error + } + } + + if (url) { + if (!validator.isURL(value)) { + error = "URL格式不合法" + return error + } + } + + return error +} \ No newline at end of file