Skip to content

Commit

Permalink
add article write/edit/view
Browse files Browse the repository at this point in the history
  • Loading branch information
sunface committed Sep 6, 2019
1 parent 123f177 commit ce09735
Show file tree
Hide file tree
Showing 43 changed files with 2,180 additions and 128 deletions.
1 change: 0 additions & 1 deletion conf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ version: 0.1.0
log_level: debug

listen_addr: ":10086"

cql:
cluster:
- "10.77.64.46:9042"
Expand Down
13 changes: 13 additions & 0 deletions internal/api_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@ package internal

import (
"github.com/labstack/echo"
"github.com/thinkindev/im.dev/internal/post"
"github.com/thinkindev/im.dev/internal/session"
)

func apiHandler(e *echo.Echo) {
// sign-in apis
e.POST("/web/signIn", session.SignIn)
e.POST("/web/signOut", session.SignOut)
e.GET("/web/user/get", session.GetUser)

// article apis
e.POST("/web/article/saveNew", post.NewArticle, session.CheckSignIn)
e.POST("/web/post/preview", post.Preview, session.CheckSignIn)
e.GET("/web/article/detail", post.GetArticleDetail)
e.GET("/web/article/beforeEdit", post.BeforeEditAr, session.CheckSignIn)
e.POST("/web/article/saveChanges", post.SaveArticleChanges, session.CheckSignIn)

// comment apis
e.POST("/web/post/comment", post.Comment, session.CheckSignIn)
e.GET("/web/post/queryComments", post.QueryComments)
}
20 changes: 19 additions & 1 deletion internal/ecode/ecode.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
package ecode

// account
// common
const (
NotSignIn = 1001
NotSignInMsg = "user need sign in"

ParamInvalid = 1002
ParamInvalidMsg = "param invalid"

CommonErrorMsg = "Oh no, an unexpected error happend"
DatabaseError = 1003

NoPermission = 1004
NoPermissionMsg = "You don't have permission"
)

// article
const (
ArticleNotFound = 1100
ArticleNotFoundMsg = "Target article not found"

PostNotFound = 1101
PostNotFoundMsg = "Target post not found"
)
3 changes: 3 additions & 0 deletions internal/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"github.com/thinkindev/im.dev/internal/misc"
"github.com/thinkindev/im.dev/internal/session"
)

// Start web server for im.dev ui
Expand All @@ -28,6 +29,8 @@ func Start(confPath string) {
}
}

session.InitUser()

e := echo.New()
e.Pre(middleware.RemoveTrailingSlash())
e.Use(middleware.GzipWithConfig(middleware.GzipConfig{Level: 5}))
Expand Down
11 changes: 11 additions & 0 deletions internal/misc/cql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package misc

import "github.com/gocql/gocql"

// CQL is the cql session for access cassandra
var CQL *gocql.Session

const (
//CQLNotFound when acess cql, get the not found result
CQLNotFound = "not found"
)
21 changes: 21 additions & 0 deletions internal/misc/id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package misc

import (
"fmt"

"github.com/sony/sonyflake"
)

// IDGen generate ditrubuted unique id
var IDGen *sonyflake.Sonyflake

func init() {
var st sonyflake.Settings
IDGen = sonyflake.NewSonyflake(st)
}

// GenID return a hex number string
func GenID() string {
id, _ := IDGen.NextID()
return fmt.Sprintf("%x", id)
}
4 changes: 0 additions & 4 deletions internal/misc/var.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package misc
import (
"encoding/base64"

"github.com/gocql/gocql"
"go.uber.org/zap"
)

Expand All @@ -15,6 +14,3 @@ var Log *zap.Logger

// Base64 is the base64 handler
var Base64 = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")

// CQL is the cql session for access cassandra
var CQL *gocql.Session
241 changes: 241 additions & 0 deletions internal/post/article.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
package post

import (
"encoding/json"
"net/http"
"time"

"github.com/labstack/echo"
"github.com/thinkindev/im.dev/internal/ecode"
"github.com/thinkindev/im.dev/internal/misc"
"github.com/thinkindev/im.dev/internal/session"
"github.com/thinkindev/im.dev/internal/utils"
"go.uber.org/zap"
)

// ArContent represent article content
type ArContent struct {
ID string `json:"id"`
Title string `json:"title"`
Tags []string `json:"tags"`
MD string `json:"md"`
Render string `json:"render"`
Lang string `json:"lang"`
Status int `json:"status"`
}

// NewArticle create a new article
func NewArticle(c echo.Context) error {
opType := c.FormValue("type")
if opType != "1" && opType != "2" {
return c.JSON(http.StatusBadRequest, misc.HTTPResp{
ErrCode: ecode.ParamInvalid,
Message: ecode.ParamInvalidMsg,
})
}

content := c.FormValue("content")
ar := &ArContent{}
err := json.Unmarshal([]byte(content), &ar)
if err != nil {
return c.JSON(http.StatusBadRequest, misc.HTTPResp{
ErrCode: ecode.ParamInvalid,
Message: ecode.ParamInvalidMsg,
})
}

sess := session.Get(c)

// generate id for article
ar.ID = misc.GenID()

// modify render
ar.Render = modify(ar.Render)

err = misc.CQL.Query(`INSERT INTO article (id,uid,title,tags,md,render,status,edit_date,lang)
VALUES (?,?,?,?,?,?,?,?,?)`, ar.ID, sess.ID, ar.Title, ar.Tags, ar.MD, ar.Render, opType, time.Now().Unix(), ar.Lang).Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}

saveTags(ar.ID, ar.Tags)
return c.JSON(http.StatusOK, misc.HTTPResp{
Data: sess.Name + "/" + ar.ID,
})
}

// ArticleDetail contains detail data of article
type ArticleDetail struct {
ID string `json:"id"`
UID string `json:"uid"`
Title string `json:"title"`
Tags []string `json:"tags"`
Render string `json:"render"`
Status int `json:"status"`
PublishDate string `json:"publish_date"`
EditDate string `json:"edit_date"`
Lang string `json:"lang"`
pubDate int64
editDate int64
}

// GetArticleDetail return detail data of the article
func GetArticleDetail(c echo.Context) error {
arID := c.FormValue("article_id")
if arID == "" {
return c.JSON(http.StatusBadRequest, misc.HTTPResp{
ErrCode: ecode.ParamInvalid,
Message: ecode.ParamInvalidMsg,
})
}

detail := &ArticleDetail{ID: arID}
err := misc.CQL.Query(`SELECT uid,title,tags,render,status,publish_date,edit_date,lang FROM article WHERE id=?`, arID).Scan(
&detail.UID, &detail.Title, &detail.Tags, &detail.Render, &detail.Status, &detail.pubDate, &detail.editDate, &detail.Lang,
)
if err != nil {
if err.Error() == misc.CQLNotFound {
return c.JSON(http.StatusNotFound, misc.HTTPResp{
ErrCode: ecode.ArticleNotFound,
Message: ecode.ArticleNotFoundMsg,
})
}
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}

if detail.pubDate != 0 {
detail.PublishDate = utils.Time2ReadableString(time.Unix(detail.pubDate, 0))
}
if detail.editDate != 0 {
detail.EditDate = utils.Time2ReadableString(time.Unix(detail.editDate, 0))
}
return c.JSON(http.StatusOK, misc.HTTPResp{
Data: detail,
})
}

// BeforeEditAr deal some pre-things before editing article
func BeforeEditAr(c echo.Context) error {
arID := c.FormValue("article_id")
if arID == "" {
return c.JSON(http.StatusBadRequest, misc.HTTPResp{
ErrCode: ecode.ParamInvalid,
Message: ecode.ParamInvalidMsg,
})
}

var uid, title, md, lang string
var tags []string
var status int
err := misc.CQL.Query(`SELECT uid,title,tags,md,lang,status FROM article WHERE id=?`, arID).Scan(
&uid, &title, &tags, &md, &lang, &status,
)
if err != nil {
if err.Error() == misc.CQLNotFound {
return c.JSON(http.StatusNotFound, misc.HTTPResp{
ErrCode: ecode.ArticleNotFound,
Message: ecode.ArticleNotFoundMsg,
})
}
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}

sess := session.Get(c)

// check whether user has permission to do so
if uid != sess.ID {
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.NoPermission,
Message: ecode.NoPermissionMsg,
})
}

ar := &ArContent{
ID: arID,
Title: title,
MD: md,
Tags: tags,
Lang: lang,
Status: status,
}

return c.JSON(http.StatusOK, misc.HTTPResp{
Data: ar,
})
}

// SaveArticleChanges save changes when edit article
func SaveArticleChanges(c echo.Context) error {
content := c.FormValue("content")
ar := &ArContent{}
err := json.Unmarshal([]byte(content), &ar)
if err != nil {
return c.JSON(http.StatusBadRequest, misc.HTTPResp{
ErrCode: ecode.ParamInvalid,
Message: ecode.ParamInvalidMsg,
})
}

// check the article is exist and user has permission
var uid string
err = misc.CQL.Query(`SELECT uid FROM article WHERE id=?`, ar.ID).Scan(&uid)
if err != nil {
if err.Error() == misc.CQLNotFound {
return c.JSON(http.StatusNotFound, misc.HTTPResp{
ErrCode: ecode.ArticleNotFound,
Message: ecode.ArticleNotFoundMsg,
})
}
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}
sess := session.Get(c)
if sess.ID != uid {
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.NoPermission,
Message: ecode.NoPermissionMsg,
})
}

// modify render
ar.Render = modify(ar.Render)

err = misc.CQL.Query(`UPDATE article SET title=?,tags=?,md=?,render=?,edit_date=?,lang=? WHERE id=?`,
ar.Title, ar.Tags, ar.MD, ar.Render, time.Now().Unix(), ar.Lang, ar.ID).Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}

saveTags(ar.ID, ar.Tags)
return c.JSON(http.StatusOK, misc.HTTPResp{
Data: sess.Name + "/" + ar.ID,
})
}

func saveTags(arID string, tags []string) {
for _, tag := range tags {
err := misc.CQL.Query(`INSERT INTO tags (name,article_id) VALUES (?,?)`, tag, arID).Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
}
}
}
Loading

0 comments on commit ce09735

Please sign in to comment.