pull/50/head
sunface 4 years ago
parent 5717798903
commit a7833a50c0

@ -3,6 +3,7 @@ common:
version: 0.1.0 version: 0.1.0
log_level: "info" log_level: "info"
is_prod: false is_prod: false
app_name: "im.dev"
#################################### Server ############################## #################################### Server ##############################
server: server:

@ -16,7 +16,7 @@ export let config = {
} }
export function initUIConfig() { export function initUIConfig() {
requestApi.get("/uiconfig").then((res) => { requestApi.get("/config").then((res) => {
console.log("初始化UI config:", res.data) console.log("初始化UI config:", res.data)
config = res.data config = res.data
})} })}

@ -69,7 +69,7 @@ import { getSvgIcon } from "components/svg-icon"
<VStack pt="6" ml={{ base: 1, md: 4, lg: 12 }} fontSize="1rem" alignItems="left"> <VStack pt="6" ml={{ base: 1, md: 4, lg: 12 }} fontSize="1rem" alignItems="left">
{navLinks.map(link => {navLinks.map(link =>
<Link href={link.url}> <Link href={link.url} key={link.title}>
<HStack cursor="pointer" px="4" py="0.7rem" rounded="md" key={link.url} color={useColorModeValue("gray.700", "whiteAlpha.900")} aria-current={asPath === link.url ? "page" : undefined} _activeLink={{ bg: useColorModeValue("transparent", "rgba(48, 140, 122, 0.3)"), color: useColorModeValue("teal.500", "teal.200"), fontWeight: "bold", }} > <HStack cursor="pointer" px="4" py="0.7rem" rounded="md" key={link.url} color={useColorModeValue("gray.700", "whiteAlpha.900")} aria-current={asPath === link.url ? "page" : undefined} _activeLink={{ bg: useColorModeValue("transparent", "rgba(48, 140, 122, 0.3)"), color: useColorModeValue("teal.500", "teal.200"), fontWeight: "bold", }} >
<Box width="25px">{link.icon}</Box><Text>{link.title}</Text> <Box width="25px">{link.icon}</Box><Text>{link.title}</Text>
</HStack> </HStack>

@ -47,7 +47,7 @@ const PostPage = () => {
}, [router]) }, [router])
const getData = async () => { const getData = async () => {
const res = await requestApi.get(`/post/${id}`) const res = await requestApi.get(`/story/post/${id}`)
setPost(res.data) setPost(res.data)
getComments(res.data.id) getComments(res.data.id)

@ -71,8 +71,11 @@ const UserPage = () => {
<HStack spacing={[0, 0, 4, 4]} mt="4" alignItems="top"> <HStack spacing={[0, 0, 4, 4]} mt="4" alignItems="top">
<VStack alignItems="left" spacing="4" width="350px" display={{ base: "none", md: "flex" }}> <VStack alignItems="left" spacing="4" width="350px" display={{ base: "none", md: "flex" }}>
<Card> <Card>
{user.about &&
<>
<Text layerStyle="textSecondary">{user.about}</Text> <Text layerStyle="textSecondary">{user.about}</Text>
<Divider my="4" /> <Divider my="4" />
</>}
{user.location && <HStack> {user.location && <HStack>
<chakra.span layerStyle="textSecondary" width="90px">Location: </chakra.span> <chakra.span layerStyle="textSecondary" width="90px">Location: </chakra.span>
<chakra.span fontWeight="500" ml="2">{user.location}</chakra.span> <chakra.span fontWeight="500" ml="2">{user.location}</chakra.span>
@ -96,9 +99,9 @@ const UserPage = () => {
{user.zhihu && <chakra.a href={user.zhihu} target="_blank"><FaZhihu /></chakra.a>} {user.zhihu && <chakra.a href={user.zhihu} target="_blank"><FaZhihu /></chakra.a>}
{user.weibo && <chakra.a href={user.weibo} target="_blank"><FaWeibo /></chakra.a>} {user.weibo && <chakra.a href={user.weibo} target="_blank"><FaWeibo /></chakra.a>}
</HStack> </HStack>
<Divider my="4" />
{user.availFor && <Box> {user.availFor && <Box>
<Divider my="4" />
<Text fontWeight="600" layerStyle="textSecondary">I am available for</Text> <Text fontWeight="600" layerStyle="textSecondary">I am available for</Text>
<Text mt="2">{user.availFor}</Text> <Text mt="2">{user.availFor}</Text>
</Box>} </Box>}

@ -47,7 +47,7 @@ function PostEditPage() {
const publish = async () => { const publish = async () => {
const res = await requestApi.post(`/admin/tag`, tag) const res = await requestApi.post(`/tag`, tag)
toast({ toast({
description: "发布成功", description: "发布成功",
status: "success", status: "success",

@ -21,7 +21,7 @@ const PostsPage = () => {
const router = useRouter() const router = useRouter()
const toast = useToast() const toast = useToast()
const getTags = () => { const getTags = () => {
requestApi.get(`/tags`).then((res) => setTags(res.data)).catch(_ => setTags([])) requestApi.get(`/tag/all`).then((res) => setTags(res.data)).catch(_ => setTags([]))
} }
useEffect(() => { useEffect(() => {
@ -33,7 +33,7 @@ const PostsPage = () => {
} }
const deleteTag= async (id) => { const deleteTag= async (id) => {
await requestApi.delete(`/admin/tag/${id}`) await requestApi.delete(`/tag/${id}`)
getTags() getTags()
toast({ toast({
description: "删除成功", description: "删除成功",

@ -43,7 +43,7 @@ import Empty from "components/empty"
}, [filter]) }, [filter])
const getBookmarkPosts = async() => { const getBookmarkPosts = async() => {
const res = await requestApi.get(`/bookmark/posts`) const res = await requestApi.get(`/story/bookmark/posts`)
setRawPosts(res.data) setRawPosts(res.data)
setPosts(res.data) setPosts(res.data)
const ts = [{id:-1,title:'All Tags',icon: 'https://cdn.hashnode.com/res/hashnode/image/upload/v1605105898259/3vuMFM8qM.png?w=200&h=200&fit=crop&crop=entropy&auto=compress&auto=compress'}] const ts = [{id:-1,title:'All Tags',icon: 'https://cdn.hashnode.com/res/hashnode/image/upload/v1605105898259/3vuMFM8qM.png?w=200&h=200&fit=crop&crop=entropy&auto=compress&auto=compress'}]

@ -28,7 +28,7 @@ function PostEditPage() {
const toast = useToast() const toast = useToast()
useEffect(() => { useEffect(() => {
if (id && id !== 'new') { if (id && id !== 'new') {
requestApi.get(`/editor/post/${id}`).then(res => setAr(res.data)) requestApi.get(`/story/post/${id}`).then(res => setAr(res.data))
} }
}, [id]) }, [id])
@ -58,7 +58,7 @@ function PostEditPage() {
} }
const publish = async () => { const publish = async () => {
const res = await requestApi.post(`/editor/post`, ar) const res = await requestApi.post(`/story/post`, ar)
toast({ toast({
description: "发布成功", description: "发布成功",
status: "success", status: "success",

@ -26,7 +26,7 @@ const PostsPage = () => {
const router = useRouter() const router = useRouter()
const toast = useToast() const toast = useToast()
const getPosts = () => { const getPosts = () => {
requestApi.get(`/editor/posts`).then((res) => setPosts(res.data)).catch(_ => setPosts([])) requestApi.get(`/story/posts/editor`).then((res) => setPosts(res.data)).catch(_ => setPosts([]))
} }
useEffect(() => { useEffect(() => {
@ -64,7 +64,7 @@ const PostsPage = () => {
} }
const submitPost = async (values, _) => { const submitPost = async (values, _) => {
await requestApi.post(`/editor/post`, values) await requestApi.post(`/story/post`, values)
onClose() onClose()
toast({ toast({
description: "提交成功", description: "提交成功",
@ -78,7 +78,7 @@ const PostsPage = () => {
const editPost = (post: Post) => { const editPost = (post: Post) => {
if (post.url.trim() === "") { if (post.url.trim() === "") {
router.push(`/editor/post/${post.id}`) router.push(`/story/post/${post.id}`)
} else { } else {
setCurrentPost(post) setCurrentPost(post)
onOpen() onOpen()
@ -86,7 +86,7 @@ const PostsPage = () => {
} }
const onDeletePost= async (id) => { const onDeletePost= async (id) => {
await requestApi.delete(`/editor/post/${id}`) await requestApi.delete(`/storyt/post/${id}`)
getPosts() getPosts()
toast({ toast({
description: "删除成功", description: "删除成功",

@ -24,7 +24,7 @@ const HomePage = () => {
const [posts, setPosts] = useState([]) const [posts, setPosts] = useState([])
const [filter, setFilter] = useState(PostFilter.Best) const [filter, setFilter] = useState(PostFilter.Best)
const initData = async () => { const initData = async () => {
const res = await requestApi.get(`/home/posts/${filter}`) const res = await requestApi.get(`/story/posts/home/${filter}`)
setPosts(res.data) setPosts(res.data)
} }
@ -86,7 +86,7 @@ export const HomeSidebar = () => {
const [posts, setPosts] = useState([]) const [posts, setPosts] = useState([])
const [filter, setFilter] = useState(PostFilter.Best) const [filter, setFilter] = useState(PostFilter.Best)
const initData = async () => { const initData = async () => {
const res = await requestApi.get(`/home/posts/${filter}`) const res = await requestApi.get(`/story/posts/home/${filter}`)
setPosts(res.data) setPosts(res.data)
} }

@ -20,7 +20,7 @@ import { useRouter } from "next/router"
const LoginPage = () => { const LoginPage = () => {
const router = useRouter() const router = useRouter()
const login = async () => { const login = async () => {
const res = await requestApi.post("/login") const res = await requestApi.post("/user/login")
saveToken(res.data.token) saveToken(res.data.token)
storage.set('session', res.data) storage.set('session', res.data)
const oldPage = storage.get('current-page') const oldPage = storage.get('current-page')

@ -298,11 +298,11 @@ const UserProfilePage = () => {
</Card> </Card>
<Box mt={6}> <Box mt={6}>
<Button <Button
// colorScheme="teal" colorScheme="cyan"
// variant="outline" variant="outline"
type="submit" type="submit"
_focus={null} _focus={null}
layerStyle="colorButton"
> >
</Button> </Button>

@ -40,7 +40,7 @@ const TagsPage = () => {
const [filter, setFilter] = useState(tagsFilter[0]) const [filter, setFilter] = useState(tagsFilter[0])
const [tags, setTags]: [Tag[], any] = useState([]) const [tags, setTags]: [Tag[], any] = useState([])
const getTags = () => { const getTags = () => {
requestApi.get(`/tags`).then((res) => setTags(res.data)).catch(_ => setTags([])) requestApi.get(`/tag/all`).then((res) => setTags(res.data)).catch(_ => setTags([]))
} }
useEffect(() => { useEffect(() => {
@ -81,7 +81,7 @@ const TagsPage = () => {
/> />
<MenuList> <MenuList>
{ {
tagsFilter.map(f => <MenuItem onClick={() => setFilter(f)}> tagsFilter.map(f => <MenuItem key={f.name} onClick={() => setFilter(f)}>
{f.name} {f.name}
</MenuItem>) </MenuItem>)
} }
@ -91,7 +91,7 @@ const TagsPage = () => {
<Divider mt="3" mb="5" /> <Divider mt="3" mb="5" />
<VStack alignItems="left" spacing="3"> <VStack alignItems="left" spacing="3">
{tags.map(t => <TagCard tag={t}/>)} {tags.map(t => <TagCard key={t.id} tag={t}/>)}
</VStack> </VStack>
</Card> </Card>
</VStack> </VStack>

@ -34,7 +34,7 @@ func SubmitComment(c *gin.Context) {
if comment.ID == "" { //add comment if comment.ID == "" { //add comment
user := user.CurrentUser(c) user := user.CurrentUser(c)
comment.CreatorID = user.ID comment.CreatorID = user.ID
comment.ID = utils.GenStoryID(models.StoryComment) comment.ID = utils.GenID(models.IDTypeComment)
err = story.AddComment(comment) err = story.AddComment(comment)
} else { // update comment } else { // update comment
err = story.EditComment(comment) err = story.EditComment(comment)
@ -78,7 +78,7 @@ func GetStoryComments(c *gin.Context) {
c.JSON(http.StatusOK, common.RespSuccess(comments)) c.JSON(http.StatusOK, common.RespSuccess(comments))
} }
func DeleteComment(c *gin.Context) { func DeleteStoryComment(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
//only admin and owner can delete comment //only admin and owner can delete comment
comment, err := story.GetComment(id) comment, err := story.GetComment(id)

@ -12,7 +12,7 @@ import (
func GetEditorPosts(c *gin.Context) { func GetEditorPosts(c *gin.Context) {
user := user.CurrentUser(c) user := user.CurrentUser(c)
ars, err := story.UserPosts(user, int64(user.ID)) ars, err := story.UserPosts(user, user.ID)
if err != nil { if err != nil {
c.JSON(err.Status, common.RespError(err.Message)) c.JSON(err.Status, common.RespError(err.Message))
return return
@ -22,7 +22,7 @@ func GetEditorPosts(c *gin.Context) {
} }
func GetUserPosts(c *gin.Context) { func GetUserPosts(c *gin.Context) {
userID, _ := strconv.ParseInt(c.Param("userID"), 10, 64) userID := c.Param("userID")
user := user.CurrentUser(c) user := user.CurrentUser(c)

@ -47,7 +47,7 @@ func DeletePost(c *gin.Context) {
c.JSON(http.StatusOK, common.RespSuccess(nil)) c.JSON(http.StatusOK, common.RespSuccess(nil))
} }
func GetPost(c *gin.Context) { func GetStoryPost(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
user := user.CurrentUser(c) user := user.CurrentUser(c)
@ -95,31 +95,3 @@ func Bookmark(c *gin.Context) {
c.JSON(http.StatusOK, common.RespSuccess(nil)) c.JSON(http.StatusOK, common.RespSuccess(nil))
} }
func GetEditorPost(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid))
return
}
user := user.CurrentUser(c)
creator, err := story.GetPostCreator(id)
if err != nil {
c.JSON(err.Status, common.RespError(err.Message))
return
}
if user.ID != creator {
c.JSON(http.StatusForbidden, common.RespError(e.NoPermission))
return
}
ar, err := story.GetPost(id, "")
if err != nil {
c.JSON(err.Status, common.RespError(err.Message))
return
}
c.JSON(http.StatusOK, common.RespSuccess(ar))
}

@ -14,7 +14,7 @@ import (
func GetTag(c *gin.Context) { func GetTag(c *gin.Context) {
name := c.Param("name") name := c.Param("name")
res, err := tags.GetTag(0, name) res, err := tags.GetTag("", name)
if err != nil { if err != nil {
c.JSON(err.Status, common.RespError(err.Message)) c.JSON(err.Status, common.RespError(err.Message))
return return

@ -36,7 +36,7 @@ func GetUserSelf(c *gin.Context) {
func GetUser(c *gin.Context) { func GetUser(c *gin.Context) {
username := c.Param("username") username := c.Param("username")
userDetail, err := user.GetUserDetail(0, username) userDetail, err := user.GetUserDetail("", username)
if err != nil { if err != nil {
c.JSON(err.Status, common.RespError(err.Message)) c.JSON(err.Status, common.RespError(err.Message))
return return

@ -8,7 +8,7 @@ import (
"github.com/imdotdev/im.dev/server/pkg/config" "github.com/imdotdev/im.dev/server/pkg/config"
) )
type UIConfig struct { type Config struct {
AppName string `json:"appName"` AppName string `json:"appName"`
CommonMaxLen int `json:"commonMaxlen"` CommonMaxLen int `json:"commonMaxlen"`
Posts *PostsConfig `json:"posts"` Posts *PostsConfig `json:"posts"`
@ -28,8 +28,8 @@ type UserConfig struct {
} }
// 在后台页面配置存储到mysql中 // 在后台页面配置存储到mysql中
func GetUIConfig(c *gin.Context) { func GetConfig(c *gin.Context) {
conf := &UIConfig{ conf := &Config{
AppName: config.Data.Common.AppName, AppName: config.Data.Common.AppName,
CommonMaxLen: 255, CommonMaxLen: 255,
Posts: &PostsConfig{ Posts: &PostsConfig{

@ -43,45 +43,39 @@ func (s *Server) Start() error {
router.Use(Cors()) router.Use(Cors())
r := router.Group("/api") r := router.Group("/api")
{
r.POST("/login", user.Login)
r.POST("/logout", user.Logout)
r.GET("/uiconfig", GetUIConfig)
}
r.GET("/post/:id", api.GetPost)
//story apis
r.GET("/story/post/:id", api.GetStoryPost)
r.POST("/story/like/:id", IsLogin(), api.LikeStory) r.POST("/story/like/:id", IsLogin(), api.LikeStory)
r.GET("/story/comments/:id", api.GetStoryComments) r.GET("/story/comments/:id", api.GetStoryComments)
r.POST("/story/comment", IsLogin(), api.SubmitComment) r.POST("/story/comment", IsLogin(), api.SubmitComment)
r.DELETE("/story/comment/:id", IsLogin(), api.DeleteStoryComment)
r.DELETE("/comment/:id", IsLogin(), api.DeleteComment) r.GET("/story/posts/editor", IsLogin(), api.GetEditorPosts)
r.GET("/story/posts/home/:filter", api.GetHomePosts)
r.GET("/editor/posts", IsLogin(), api.GetEditorPosts) r.POST("/story/post", IsLogin(), api.SubmitPost)
r.POST("/editor/post", IsLogin(), api.SubmitPost) r.DELETE("/story/post/:id", IsLogin(), api.DeletePost)
r.DELETE("/editor/post/:id", IsLogin(), api.DeletePost) r.POST("/story/bookmark/:storyID", IsLogin(), api.Bookmark)
r.GET("/editor/post/:id", IsLogin(), api.GetEditorPost) r.GET("/story/bookmark/posts", IsLogin(), api.GetBookmarkPosts)
r.POST("/admin/tag", IsLogin(), api.SubmitTag) // tag apis
r.DELETE("/admin/tag/:id", IsLogin(), api.DeleteTag) r.POST("/tag", IsLogin(), api.SubmitTag)
r.DELETE("/tag/:id", IsLogin(), api.DeleteTag)
r.GET("/tags", api.GetTags) r.GET("/tag/all", api.GetTags)
r.GET("/tag/posts/:id", api.GetTagPosts) r.GET("/tag/posts/:id", api.GetTagPosts)
r.GET("/tag/info/:name", api.GetTag) r.GET("/tag/info/:name", api.GetTag)
r.GET("/users", api.GetUsers) // user apis
r.GET("/user/all", api.GetUsers)
r.GET("/user/self", IsLogin(), api.GetUserSelf) r.GET("/user/self", IsLogin(), api.GetUserSelf)
r.GET("/user/info/:username", api.GetUser) r.GET("/user/info/:username", api.GetUser)
r.POST("/user/update", IsLogin(), api.UpdateUser) r.POST("/user/update", IsLogin(), api.UpdateUser)
r.GET("/user/posts/:userID", api.GetUserPosts) r.GET("/user/posts/:userID", api.GetUserPosts)
r.GET("/user/session", IsLogin(), api.GetSession)
r.POST("/user/login", user.Login)
r.POST("/user/logout", user.Logout)
r.GET("/home/posts/:filter", api.GetHomePosts) // other apis
r.GET("/config", GetConfig)
r.GET("/session", IsLogin(), api.GetSession)
r.POST("/bookmark/:storyID", IsLogin(), api.Bookmark)
r.GET("/bookmark/posts", IsLogin(), api.GetBookmarkPosts)
err := router.Run(config.Data.Server.Addr) err := router.Run(config.Data.Server.Addr)
if err != nil { if err != nil {

@ -2,7 +2,7 @@ package storage
var sqlTables = map[string]string{ var sqlTables = map[string]string{
"user": `CREATE TABLE IF NOT EXISTS user ( "user": `CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY AUTOINCREMENT, id VARCHAR(255) PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE, username VARCHAR(255) NOT NULL UNIQUE,
nickname VARCHAR(255) DEFAULT '', nickname VARCHAR(255) DEFAULT '',
avatar VARCHAR(255) DEFAULT '', avatar VARCHAR(255) DEFAULT '',
@ -23,7 +23,7 @@ var sqlTables = map[string]string{
ON user (email);`, ON user (email);`,
"user_profile": `CREATE TABLE IF NOT EXISTS user_profile ( "user_profile": `CREATE TABLE IF NOT EXISTS user_profile (
id INTEGER PRIMARY KEY, id VARCHAR(255) PRIMARY KEY,
tagline VARCHAR(255), tagline VARCHAR(255),
cover VARCHAR(255), cover VARCHAR(255),
@ -43,7 +43,7 @@ var sqlTables = map[string]string{
);`, );`,
"user_skills": `CREATE TABLE IF NOT EXISTS user_skills ( "user_skills": `CREATE TABLE IF NOT EXISTS user_skills (
user_id INTEGER, user_id VARCHAR(255),
skill_id INTEGER skill_id INTEGER
); );
CREATE INDEX IF NOT EXISTS user_skills_userid CREATE INDEX IF NOT EXISTS user_skills_userid
@ -54,13 +54,13 @@ var sqlTables = map[string]string{
"sessions": `CREATE TABLE IF NOT EXISTS sessions ( "sessions": `CREATE TABLE IF NOT EXISTS sessions (
sid VARCHAR(255) primary key, sid VARCHAR(255) primary key,
user_id INTEGER user_id VARCHAR(255)
); );
`, `,
"posts": `CREATE TABLE IF NOT EXISTS posts ( "posts": `CREATE TABLE IF NOT EXISTS posts (
id VARCHAR(255) PRIMARY KEY, id VARCHAR(255) PRIMARY KEY,
creator INTEGER NOT NULL, creator VARCHAR(255) NOT NULL,
slug VARCHAR(64) NOT NULL, slug VARCHAR(64) NOT NULL,
title VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL,
md TEXT, md TEXT,
@ -82,21 +82,21 @@ var sqlTables = map[string]string{
ON posts (creator, slug); ON posts (creator, slug);
`, `,
"like": `CREATE TABLE IF NOT EXISTS like ( "likes": `CREATE TABLE IF NOT EXISTS likes (
id VARCHAR(255), user_id VARCHAR(255),
user_id INTEGER, story_id VARCHAR(255),
created DATETIME NOT NULL created DATETIME NOT NULL
); );
CREATE INDEX IF NOT EXISTS like_id CREATE INDEX IF NOT EXISTS likes_userid
ON like (id); ON likes (user_id);
CREATE INDEX IF NOT EXISTS like_userid CREATE INDEX IF NOT EXISTS likes_storyid
ON like (user_id); ON likes (story_id);
`, `,
"tags": `CREATE TABLE IF NOT EXISTS tags ( "tags": `CREATE TABLE IF NOT EXISTS tags (
id INTEGER PRIMARY KEY AUTOINCREMENT, id VARCHAR(255) PRIMARY KEY,
creator INTEGER NOT NULL, creator VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
icon VARCHAR(255), icon VARCHAR(255),
cover VARCHAR(255), cover VARCHAR(255),
@ -112,7 +112,7 @@ var sqlTables = map[string]string{
`, `,
"tag_post": `CREATE TABLE IF NOT EXISTS tag_post ( "tag_post": `CREATE TABLE IF NOT EXISTS tag_post (
tag_id INTEGER, tag_id VARCHAR(255),
post_id VARCHAR(255) post_id VARCHAR(255)
); );
CREATE INDEX IF NOT EXISTS tag_post_tagid CREATE INDEX IF NOT EXISTS tag_post_tagid
@ -124,7 +124,7 @@ var sqlTables = map[string]string{
"comments": `CREATE TABLE IF NOT EXISTS comments ( "comments": `CREATE TABLE IF NOT EXISTS comments (
id VARCHAR(255) PRIMARY KEY, id VARCHAR(255) PRIMARY KEY,
target_id VARCHAR(255), target_id VARCHAR(255),
creator INTEGER, creator VARCHAR(255),
MD TEXT, MD TEXT,
likes INTEGER DEFAULT 0, likes INTEGER DEFAULT 0,
created DATETIME NOT NULL, created DATETIME NOT NULL,
@ -143,7 +143,7 @@ var sqlTables = map[string]string{
`, `,
"bookmarks": `CREATE TABLE IF NOT EXISTS bookmarks ( "bookmarks": `CREATE TABLE IF NOT EXISTS bookmarks (
user_id INTEGER, user_id VARCHAR(255),
story_id VARCHAR(255), story_id VARCHAR(255),
created DATETIME created DATETIME
); );

@ -8,7 +8,7 @@ import (
"github.com/imdotdev/im.dev/server/pkg/e" "github.com/imdotdev/im.dev/server/pkg/e"
) )
func Bookmark(userID int64, storyID string) *e.Error { func Bookmark(userID string, storyID string) *e.Error {
storyExist := Exist(storyID) storyExist := Exist(storyID)
if !storyExist { if !storyExist {
return e.New(http.StatusNotFound, e.NotFound) return e.New(http.StatusNotFound, e.NotFound)
@ -36,7 +36,7 @@ func Bookmark(userID int64, storyID string) *e.Error {
return nil return nil
} }
func Bookmarked(userID int64, storyID string) (bool, error) { func Bookmarked(userID string, storyID string) (bool, error) {
var nid string var nid string
err := db.Conn.QueryRow("select story_id from bookmarks where user_id=? and story_id=?", userID, storyID).Scan(&nid) err := db.Conn.QueryRow("select story_id from bookmarks where user_id=? and story_id=?", userID, storyID).Scan(&nid)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {

@ -195,9 +195,9 @@ func GetStoryIDByCommentID(cid string) (string, bool, error) {
} }
switch targetID[:1] { switch targetID[:1] {
case models.StoryPost: case models.IDTypePost:
return targetID, true, nil return targetID, true, nil
case models.StoryComment: case models.IDTypeComment:
var nid string var nid string
err := db.Conn.QueryRow("select target_id from comments where id=?", targetID).Scan(&nid) err := db.Conn.QueryRow("select target_id from comments where id=?", targetID).Scan(&nid)
if err != nil { if err != nil {

@ -10,7 +10,7 @@ import (
"github.com/imdotdev/im.dev/server/pkg/e" "github.com/imdotdev/im.dev/server/pkg/e"
) )
func Like(storyID string, userId int64) *e.Error { func Like(storyID string, userId string) *e.Error {
exist := Exist(storyID) exist := Exist(storyID)
if !exist { if !exist {
return e.New(http.StatusNotFound, e.NotFound) return e.New(http.StatusNotFound, e.NotFound)
@ -22,14 +22,14 @@ func Like(storyID string, userId int64) *e.Error {
if liked { if liked {
// 已经喜欢过该篇文章,更改为不喜欢 // 已经喜欢过该篇文章,更改为不喜欢
_, err := db.Conn.Exec("DELETE FROM like WHERE id=? and user_id=?", storyID, userId) _, err := db.Conn.Exec("DELETE FROM likes WHERE story_id=? and user_id=?", storyID, userId)
if err != nil { if err != nil {
return e.New(http.StatusInternalServerError, e.Internal) return e.New(http.StatusInternalServerError, e.Internal)
} }
db.Conn.Exec(fmt.Sprintf("UPDATE %s SET likes=likes-1 WHERE id=?", tbl), storyID) db.Conn.Exec(fmt.Sprintf("UPDATE %s SET likes=likes-1 WHERE id=?", tbl), storyID)
} else { } else {
_, err := db.Conn.Exec("INSERT INTO like (id,user_id,created) VALUES (?,?,?)", storyID, userId, time.Now()) _, err := db.Conn.Exec("INSERT INTO likes (story_id,user_id,created) VALUES (?,?,?)", storyID, userId, time.Now())
if err != nil { if err != nil {
logger.Warn("add like error", "error", err) logger.Warn("add like error", "error", err)
return e.New(http.StatusInternalServerError, e.Internal) return e.New(http.StatusInternalServerError, e.Internal)
@ -40,10 +40,10 @@ func Like(storyID string, userId int64) *e.Error {
return nil return nil
} }
func GetLiked(storyID string, userID int64) bool { func GetLiked(storyID string, userID string) bool {
liked := false liked := false
var nid string var nid string
err := db.Conn.QueryRow("SELECT id FROM like WHERE id=? and user_id=?", storyID, userID).Scan(&nid) err := db.Conn.QueryRow("SELECT story_id FROM likes WHERE story_id=? and user_id=?", storyID, userID).Scan(&nid)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
logger.Warn("query story like error", "error", err) logger.Warn("query story like error", "error", err)
return false return false

@ -70,7 +70,7 @@ func SubmitPost(c *gin.Context) (map[string]string, *e.Error) {
setSlug(user.ID, post) setSlug(user.ID, post)
if post.ID == "" { if post.ID == "" {
post.ID = utils.GenStoryID(models.StoryPost) post.ID = utils.GenID(models.IDTypePost)
//create //create
_, err := db.Conn.Exec("INSERT INTO posts (id,creator,slug, title, md, url, cover, brief,status, created, updated) VALUES(?,?,?,?,?,?,?,?,?,?,?)", _, err := db.Conn.Exec("INSERT INTO posts (id,creator,slug, title, md, url, cover, brief,status, created, updated) VALUES(?,?,?,?,?,?,?,?,?,?,?)",
post.ID, user.ID, post.Slug, post.Title, md, post.URL, post.Cover, post.Brief, models.StatusPublished, now, now) post.ID, user.ID, post.Slug, post.Title, md, post.URL, post.Cover, post.Brief, models.StatusPublished, now, now)
@ -148,7 +148,7 @@ func GetPost(id string, slug string) (*models.Post, *e.Error) {
err = ar.Creator.Query() err = ar.Creator.Query()
// get tags // get tags
t := make([]int64, 0) t := make([]string, 0)
rows, err := db.Conn.Query("SELECT tag_id FROM tag_post WHERE post_id=?", ar.ID) rows, err := db.Conn.Query("SELECT tag_id FROM tag_post WHERE post_id=?", ar.ID)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
return nil, e.New(http.StatusInternalServerError, e.Internal) return nil, e.New(http.StatusInternalServerError, e.Internal)
@ -156,7 +156,7 @@ func GetPost(id string, slug string) (*models.Post, *e.Error) {
ar.RawTags = make([]*models.Tag, 0) ar.RawTags = make([]*models.Tag, 0)
for rows.Next() { for rows.Next() {
var tag int64 var tag string
err = rows.Scan(&tag) err = rows.Scan(&tag)
t = append(t, tag) t = append(t, tag)
@ -177,15 +177,15 @@ func GetPost(id string, slug string) (*models.Post, *e.Error) {
return ar, nil return ar, nil
} }
func GetPostCreator(id string) (int64, *e.Error) { func GetPostCreator(id string) (string, *e.Error) {
var uid int64 var uid string
err := db.Conn.QueryRow("SELECT creator FROM posts WHERE id=?", id).Scan(&uid) err := db.Conn.QueryRow("SELECT creator FROM posts WHERE id=?", id).Scan(&uid)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return 0, e.New(http.StatusNotFound, e.NotFound) return "", e.New(http.StatusNotFound, e.NotFound)
} }
logger.Warn("get post creator error", "error", err) logger.Warn("get post creator error", "error", err)
return 0, e.New(http.StatusInternalServerError, e.Internal) return "", e.New(http.StatusInternalServerError, e.Internal)
} }
return uid, nil return uid, nil
@ -210,7 +210,7 @@ func postExist(id string) bool {
// 1. 长度不能超过127 // 1. 长度不能超过127
// 2. 每次title更新都要重新生成slug // 2. 每次title更新都要重新生成slug
// 3. 单个用户下的slug不能重复如果已经存在需要加上-1这种字符 // 3. 单个用户下的slug不能重复如果已经存在需要加上-1这种字符
func setSlug(creator int64, post *models.Post) error { func setSlug(creator string, post *models.Post) error {
slug := utils.Slugify(post.Title) slug := utils.Slugify(post.Title)
if len(slug) > 100 { if len(slug) > 100 {
slug = slug[:100] slug = slug[:100]

@ -27,7 +27,7 @@ func HomePosts(user *models.User, filter string) (models.Posts, *e.Error) {
return posts, nil return posts, nil
} }
func UserPosts(user *models.User, uid int64) (models.Posts, *e.Error) { func UserPosts(user *models.User, uid string) (models.Posts, *e.Error) {
rows, err := db.Conn.Query("select id,slug,title,url,cover,brief,likes,views,creator,created,updated from posts where creator=?", uid) rows, err := db.Conn.Query("select id,slug,title,url,cover,brief,likes,views,creator,created,updated from posts where creator=?", uid)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
logger.Warn("get user posts error", "error", err) logger.Warn("get user posts error", "error", err)

@ -9,9 +9,9 @@ var logger = log.RootLogger.New("logger", "story")
func Exist(id string) bool { func Exist(id string) bool {
switch id[:1] { switch id[:1] {
case models.StoryPost: case models.IDTypePost:
return postExist(id) return postExist(id)
case models.StoryComment: case models.IDTypeComment:
return commentExist(id) return commentExist(id)
default: default:
return false return false
@ -20,9 +20,9 @@ func Exist(id string) bool {
func getStorySqlTable(id string) string { func getStorySqlTable(id string) string {
switch id[:1] { switch id[:1] {
case models.StoryPost: case models.IDTypePost:
return "posts" return "posts"
case models.StoryComment: case models.IDTypeComment:
return "comments" return "comments"
default: default:
return "unknown" return "unknown"

@ -42,10 +42,11 @@ func SubmitTag(tag *models.Tag) *e.Error {
md := utils.Compress(tag.Md) md := utils.Compress(tag.Md)
if tag.ID == 0 { if tag.ID == "" {
tag.ID = utils.GenID(models.IDTypeTag)
//create //create
_, err := db.Conn.Exec("INSERT INTO tags (creator,name, title, md, icon, cover, created, updated) VALUES(?,?,?,?,?,?,?,?)", _, err := db.Conn.Exec("INSERT INTO tags (id,creator,name, title, md, icon, cover, created, updated) VALUES(?,?,?,?,?,?,?,?,?)",
tag.Creator, tag.Name, tag.Title, md, tag.Icon, tag.Cover, now, now) tag.ID, tag.Creator, tag.Name, tag.Title, md, tag.Icon, tag.Cover, now, now)
if err != nil { if err != nil {
if e.IsErrUniqueConstraint(err) { if e.IsErrUniqueConstraint(err) {
return e.New(http.StatusConflict, "同样的Tag name已存在") return e.New(http.StatusConflict, "同样的Tag name已存在")
@ -111,7 +112,7 @@ func DeleteTag(id int64) *e.Error {
return nil return nil
} }
func GetTag(id int64, name string) (*models.Tag, *e.Error) { func GetTag(id string, name string) (*models.Tag, *e.Error) {
tag := &models.Tag{} tag := &models.Tag{}
var rawmd []byte var rawmd []byte
err := db.Conn.QueryRow("SELECT id,creator,title,name,icon,cover,created,updated,md from tags where id=? or name=?", id, name).Scan( err := db.Conn.QueryRow("SELECT id,creator,title,name,icon,cover,created,updated,md from tags where id=? or name=?", id, name).Scan(

@ -24,7 +24,7 @@ type Session struct {
func Login(c *gin.Context) { func Login(c *gin.Context) {
user := &models.User{} user := &models.User{}
err := user.Query(0, config.Data.User.SuperAdminUsername, "") err := user.Query("", config.Data.User.SuperAdminUsername, "")
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
c.String(http.StatusNotFound, "") c.String(http.StatusNotFound, "")
@ -132,7 +132,7 @@ func GetSession(c *gin.Context) *Session {
} }
func loadSession(sid string) *Session { func loadSession(sid string) *Session {
var userid int64 var userid string
q := `SELECT user_id FROM sessions WHERE sid=?` q := `SELECT user_id FROM sessions WHERE sid=?`
err := db.Conn.QueryRow(q, sid).Scan(&userid) err := db.Conn.QueryRow(q, sid).Scan(&userid)
if err != nil { if err != nil {

@ -32,7 +32,7 @@ func GetUsers(q string) ([]*models.User, *e.Error) {
return users, nil return users, nil
} }
func GetUserDetail(id int64, username string) (*models.User, *e.Error) { func GetUserDetail(id string, username string) (*models.User, *e.Error) {
user := &models.User{} user := &models.User{}
err := user.Query(id, username, "") err := user.Query(id, username, "")
if err != nil { if err != nil {
@ -54,14 +54,14 @@ func GetUserDetail(id int64, username string) (*models.User, *e.Error) {
} }
// get user skills // get user skills
user.Skills = make([]int64, 0) user.Skills = make([]string, 0)
user.RawSkills = make([]*models.Tag, 0) user.RawSkills = make([]*models.Tag, 0)
rows, err := db.Conn.Query("SELECT skill_id from user_skills WHERE user_id=?", user.ID) rows, err := db.Conn.Query("SELECT skill_id from user_skills WHERE user_id=?", user.ID)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
logger.Warn("query user skills error", "error", err) logger.Warn("query user skills error", "error", err)
} }
for rows.Next() { for rows.Next() {
var skill int64 var skill string
rows.Scan(&skill) rows.Scan(&skill)
user.Skills = append(user.Skills, skill) user.Skills = append(user.Skills, skill)

@ -5,7 +5,7 @@ import "time"
type Comment struct { type Comment struct {
ID string `json:"id"` ID string `json:"id"`
TargetID string `json:"targetID"` // 被评论的文章、书籍等ID TargetID string `json:"targetID"` // 被评论的文章、书籍等ID
CreatorID int64 `json:"creatorID"` CreatorID string `json:"creatorID"`
Creator *UserSimple `json:"creator"` Creator *UserSimple `json:"creator"`
Md string `json:"md"` Md string `json:"md"`
Likes int `json:"likes"` Likes int `json:"likes"`

@ -0,0 +1,8 @@
package models
const (
IDTypePost = "1"
IDTypeComment = "2"
IDTypeUser = "3"
IDTypeTag = "4"
)

@ -11,14 +11,14 @@ const (
type Post struct { type Post struct {
ID string `json:"id"` ID string `json:"id"`
Creator *UserSimple `json:"creator"` Creator *UserSimple `json:"creator"`
CreatorID int64 `json:"creatorId"` CreatorID string `json:"creatorId"`
Title string `json:"title"` Title string `json:"title"`
Slug string `json:"slug"` Slug string `json:"slug"`
Md string `json:"md"` Md string `json:"md"`
URL string `json:"url"` URL string `json:"url"`
Cover string `json:"cover"` Cover string `json:"cover"`
Brief string `json:"brief"` Brief string `json:"brief"`
Tags []int64 `json:"tags"` Tags []string `json:"tags"`
RawTags []*Tag `json:"rawTags"` RawTags []*Tag `json:"rawTags"`
Likes int `json:"likes"` Likes int `json:"likes"`
Liked bool `json:"liked"` Liked bool `json:"liked"`

@ -1,6 +0,0 @@
package models
const (
StoryPost = "1"
StoryComment = "2"
)

@ -3,8 +3,8 @@ package models
import "time" import "time"
type Tag struct { type Tag struct {
ID int64 `json:"id"` ID string `json:"id"`
Creator int64 `json:"creator,omitempty"` Creator string `json:"creator,omitempty"`
Title string `json:"title"` Title string `json:"title"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Md string `json:"md,omitempty"` Md string `json:"md,omitempty"`

@ -7,20 +7,20 @@ import (
) )
type User struct { type User struct {
ID int64 `json:"id"` ID string `json:"id"`
Username string `json:"username"` Username string `json:"username"`
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Email string `json:"email"` Email string `json:"email"`
Role RoleType `json:"role"` Role RoleType `json:"role"`
Tagline string `json:"tagline"` Tagline string `json:"tagline"`
Cover string `json:"cover"` Cover string `json:"cover"`
Location string `json:"location"` Location string `json:"location"`
AvailFor string `json:"availFor"` AvailFor string `json:"availFor"`
About string `json:"about"` About string `json:"about"`
RawSkills []*Tag `json:"rawSkills"` RawSkills []*Tag `json:"rawSkills"`
Skills []int64 `json:"skills"` Skills []string `json:"skills"`
Website string `json:"website"` Website string `json:"website"`
Twitter string `json:"twitter"` Twitter string `json:"twitter"`
@ -34,7 +34,7 @@ type User struct {
Created time.Time `json:"created"` Created time.Time `json:"created"`
} }
func (user *User) Query(id int64, username string, email string) error { func (user *User) Query(id string, username string, email string) error {
err := db.Conn.QueryRow(`SELECT id,username,role,nickname,email,avatar,last_seen_at,created FROM user WHERE id=? or username=? or email=?`, err := db.Conn.QueryRow(`SELECT id,username,role,nickname,email,avatar,last_seen_at,created FROM user WHERE id=? or username=? or email=?`,
id, username, email).Scan(&user.ID, &user.Username, &user.Role, &user.Nickname, &user.Email, &user.Avatar, &user.LastSeenAt, &user.Created) id, username, email).Scan(&user.ID, &user.Username, &user.Role, &user.Nickname, &user.Email, &user.Avatar, &user.LastSeenAt, &user.Created)
@ -46,7 +46,7 @@ func (user *User) Query(id int64, username string, email string) error {
} }
type UserSimple struct { type UserSimple struct {
ID int64 `json:"id"` ID string `json:"id"`
Username string `json:"username"` Username string `json:"username"`
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`

@ -2,7 +2,7 @@ package utils
import "github.com/lithammer/shortuuid/v3" import "github.com/lithammer/shortuuid/v3"
func GenStoryID(storyType string) string { func GenID(idType string) string {
u := shortuuid.New() u := shortuuid.New()
return storyType + u return idType + u
} }

@ -36,7 +36,7 @@ export const CommentCard = (props: Props) => {
} }
const deleteComment = async id => { const deleteComment = async id => {
await requestApi.delete(`/comment/${id}`) await requestApi.delete(`/story/comment/${id}`)
onChange() onChange()
} }

@ -37,7 +37,7 @@ export const Reply = (props: Props) => {
} }
const deleteReply = async id => { const deleteReply = async id => {
await requestApi.delete(`/comment/${id}`) await requestApi.delete(`/story/comment/${id}`)
onChange() onChange()
} }

@ -42,7 +42,7 @@ export function MarkdownEditor(props: Props) {
useEffect(() => { useEffect(() => {
if (at !== '') { if (at !== '') {
requestApi.get(`/users?query=${at.trim()}`).then(res => setAtUsers(res.data)) requestApi.get(`/user/all?query=${at.trim()}`).then(res => setAtUsers(res.data))
} }
},[at]) },[at])

@ -15,7 +15,7 @@ const Bookmark = (props: Props) => {
const [bookmarked,setBookmarked] = useState(props.bookmarked) const [bookmarked,setBookmarked] = useState(props.bookmarked)
const bookmark = async () => { const bookmark = async () => {
await requestApi.post(`/bookmark/${storyID}`) await requestApi.post(`/story/bookmark/${storyID}`)
setBookmarked(!bookmarked) setBookmarked(!bookmarked)
} }

@ -6,6 +6,7 @@ import Link from "next/link"
import UnicornLike from "./like" import UnicornLike from "./like"
import { FaHeart, FaRegBookmark, FaRegComment, FaRegHeart } from "react-icons/fa" import { FaHeart, FaRegBookmark, FaRegComment, FaRegHeart } from "react-icons/fa"
import SvgButton from "components/svg-button" import SvgButton from "components/svg-button"
import Bookmark from "./bookmark"
interface Props { interface Props {
post: Post post: Post
@ -40,7 +41,7 @@ export const SimplePostCard = (props: Props) => {
<SvgButton icon="bookmark" height="1rem" onClick={null} style={{marginLeft: '4px'}}/> <Box style={{marginLeft: '4px'}}><Bookmark storyID={post.id} bookmarked={post.bookmarked} height=".95rem"/></Box>
</HStack> </HStack>
</VStack> </VStack>
) )

@ -18,7 +18,7 @@ export const Tags = (props: Props) => {
const [tags, setTags]: [Tag[], any] = useState([]) const [tags, setTags]: [Tag[], any] = useState([])
useEffect(() => { useEffect(() => {
requestApi.get('/tags').then(res => { requestApi.get('/tag/all').then(res => {
setOptions(res.data) setOptions(res.data)
const t = [] const t = []
props.tags?.forEach(id => { props.tags?.forEach(id => {
@ -60,7 +60,7 @@ export const Tags = (props: Props) => {
{tags.length > 0 && <Box mt={props.size === 'lg' ? 4 : 2}> {tags.length > 0 && <Box mt={props.size === 'lg' ? 4 : 2}>
{ {
tags.map(tag => tags.map(tag =>
<ChakraTag key={tag.id} mr="2" colorScheme="teal" variant="solid" px="2" py={props.size === 'lg' ? 2 : 1}> <ChakraTag key={tag.id} mr="2" colorScheme="cyan" variant="solid" px="2" py={props.size === 'lg' ? 2 : 1}>
<TagLabel>{tag.title}</TagLabel> <TagLabel>{tag.title}</TagLabel>
<TagCloseButton onClick={_ => removeTag(tag)} /> <TagCloseButton onClick={_ => removeTag(tag)} />
</ChakraTag>) </ChakraTag>)

@ -11,7 +11,7 @@ function useSession(): Session{
if (sess) { if (sess) {
setSession(sess) setSession(sess)
// 页面重新进入时,跟服务器端进行信息同步 // 页面重新进入时,跟服务器端进行信息同步
requestApi.get(`/session`).then(res => { requestApi.get(`/user/session`).then(res => {
setSession(res.data) setSession(res.data)
}) })
} }

@ -3,7 +3,7 @@ import { requestApi } from "./axios/request"
import events from "./events" import events from "./events"
export const logout = async () => { export const logout = async () => {
await requestApi.post("/logout") await requestApi.post("/user/logout")
removeToken() removeToken()
events.emit('set-session', null) events.emit('set-session', null)
} }

@ -44,6 +44,7 @@ const customTheme = extendTheme({
}, },
body: { body: {
background: mode("white","gray.800" )(props), background: mode("white","gray.800" )(props),
minHeight: '100vh',
color: mode("gray.700", "whiteAlpha.900")(props), color: mode("gray.700", "whiteAlpha.900")(props),
".deleted": { ".deleted": {
color: "#ff8383 !important", color: "#ff8383 !important",

Loading…
Cancel
Save