예제 #1
0
파일: front.go 프로젝트: caixw/typing
// 首页或是列表页
func pagePosts(w http.ResponseWriter, r *http.Request) {
	info, err := getInfo()
	if err != nil {
		logs.Error("pagePosts:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}

	page := conv.MustInt(r.FormValue("page"), 1)
	if page == 1 {
		info.Canonical = app.URL(app.HomeURL())
	} else if page > 1 { // 为1的时候,不需要prev
		info.Canonical = app.URL(app.PostsURL(page))
		info.PrevPage = &Anchor{Title: "上一页", Link: app.PostsURL(page - 1)}
	}

	if page*opt.SidebarSize < info.PostSize {
		info.NextPage = &Anchor{Title: "下一页", Link: app.PostsURL(page + 1)}
	}

	posts, err := getPosts(page - 1)
	if err != nil {
		logs.Error("pagePosts:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}

	data := map[string]interface{}{
		"info":  info,
		"posts": posts,
	}
	render(w, r, "posts", data, map[string]string{"Content-Type": "text/html"})
}
예제 #2
0
파일: posts.go 프로젝트: caixw/typing
func adminSetPostState(w http.ResponseWriter, r *http.Request, state int) {
	id, ok := util.ParamID(w, r, "id")
	if !ok {
		return
	}

	p := &models.Post{ID: id}
	if err := db.Select(p); err != nil {
		logs.Error("adminSetPostState:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	// 不可能存在状态值为0的文章,出现此值,表明数据库没有该条记录
	if p.State == models.PostStateAll {
		util.RenderJSON(w, http.StatusNotFound, nil, nil)
		return
	}

	p = &models.Post{ID: id, State: state}
	if _, err := db.Update(p); err != nil {
		logs.Error("adminSetPostState:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if err := stats.UpdatePostsSize(); err != nil {
		logs.Error("admin.adminSetPostState:", err)
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusCreated, "{}", nil)
}
예제 #3
0
파일: front.go 프로젝트: caixw/typing
// /tags
func pageTags(w http.ResponseWriter, r *http.Request) {
	info, err := getInfo()
	if err != nil {
		logs.Error("pageTags:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}
	info.Canonical = app.URL(app.TagsURL())
	info.Title = "标签"

	sql := `SELECT {id} AS {ID}, {name} AS {Name}, {title} AS {Title} FROM #tags`
	rows, err := db.Query(true, sql)
	if err != nil {
		logs.Error("pageTags:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	tags := make([]*Tag, 0, 100)
	if _, err = fetch.Obj(&tags, rows); err != nil {
		logs.Error("pageTags:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}

	data := map[string]interface{}{"info": info, "tags": tags}
	render(w, r, "tags", data, map[string]string{"Content-Type": "text/html"})
}
예제 #4
0
파일: posts.go 프로젝트: caixw/typing
// @api get /admin/api/posts 获取文章列表
// @apiQuery page  int 页码,从0开始
// @apiQuery size  int 显示尺寸
// @apiQuery state int 状态
// @apiGroup admin
//
// @apiSuccess ok 200
// @apiParam count int   符合条件的所有记录数量,不包含page和size条件
// @apiParam posts array 当前页的记录数量
func adminGetPosts(w http.ResponseWriter, r *http.Request) {
	var page, size, state int
	var ok bool
	if state, ok = util.QueryInt(w, r, "state", models.CommentStateAll); !ok {
		return
	}

	sql := db.SQL().Table("#posts")
	if state != models.PostStateAll {
		sql.And("{state}=?", state)
	}
	count, err := sql.Count(true)
	if err != nil {
		logs.Error("adminGetPosts:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if page, ok = util.QueryInt(w, r, "page", 0); !ok {
		return
	}
	if size, ok = util.QueryInt(w, r, "size", opt.PageSize); !ok {
		return
	}
	sql.Limit(size, page*size)
	maps, err := sql.SelectMapString(true, "*")
	if err != nil {
		logs.Error("adminGetPosts:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	util.RenderJSON(w, http.StatusOK, map[string]interface{}{"count": count, "posts": maps}, nil)
}
예제 #5
0
파일: theme.go 프로젝트: caixw/typing
// @api put /admin/api/themes/current 更改当前的主题
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
// @apiParam value string 新值
//
// @apiSuccess 200 OK
func adminPutCurrentTheme(w http.ResponseWriter, r *http.Request) {
	v := &struct {
		Value string `json:"value"`
	}{}
	if !util.ReadJSON(w, r, v) {
		return
	}

	if len(v.Value) == 0 {
		util.RenderJSON(w, http.StatusBadRequest, &util.ErrorResult{Message: "必须指定一个值!"}, nil)
		return
	}

	if err := app.SetOption("theme", v.Value, false); err != nil {
		logs.Error("adminPutTheme:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if err := front.Switch(v.Value); err != nil {
		logs.Error("adminPutTheme:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	lastUpdated()
	util.RenderJSON(w, http.StatusNoContent, nil, nil)
}
예제 #6
0
파일: front.go 프로젝트: caixw/typing
// /tags/1.html
func pageTag(w http.ResponseWriter, r *http.Request) {
	tagName, ok := util.ParamString(w, r, "id")
	if !ok {
		return
	}
	tagName = strings.TrimSuffix(tagName, opt.Suffix)

	sql := `SELECT {id} AS {ID}, {name} AS {Name}, {title} AS {Title}, {description} AS {Description}
	FROM #tags
	WHERE {name}=?`
	rows, err := db.Query(true, sql, tagName)
	if err != nil {
		logs.Error("pageTag:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	tag := &Tag{}
	if _, err = fetch.Obj(tag, rows); err != nil {
		logs.Error("pageTag:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}

	info, err := getInfo()
	if err != nil {
		logs.Error("pageTag:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}

	info.Canonical = app.URL(tag.Permalink())
	info.Title = tag.Title

	page := conv.MustInt(r.FormValue("page"), 1)
	if page < 1 { // 不能小于1
		page = 1
	} else if page > 1 { // 为1的时候,不需要prev
		info.PrevPage = &Anchor{Title: "上一页", Link: app.TagURL(tagName, page-1)}
	}
	if page*opt.SidebarSize < tag.Count() {
		info.NextPage = &Anchor{Title: "下一页", Link: app.TagURL(tagName, page+1)}
	}
	posts, err := getTagPosts(page-1, tag.ID)
	if err != nil {
		logs.Error("pageTag:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}

	data := map[string]interface{}{
		"info":  info,
		"tag":   tag,
		"posts": posts,
	}
	render(w, r, "tag", data, map[string]string{"Content-Type": "text/html"})
}
예제 #7
0
파일: tags.go 프로젝트: caixw/typing
// @api put /admin/api/tags/{id} 修改某id的标签内容
// @apiParam id int 需要修改的标签id
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
// @apiParam name        string 唯一名称
// @apiParam title       string 显示的标题
// @apiParam description string 描述信息,可以是html
// @apiExample json
// {
//     "name": "tag-1",
//     "title":"标签1",
//     "description": "<h1>desc</h1>"
// }
//
// @apiSuccess 204 no content
//
// @apiError 400 bad request
// @apiParam message string 错误信息
// @apiParam detail  array  说细的错误信息,用于描述哪个字段有错
// @apiExample json
// {
//     "message": "格式错误",
//     "detail":[
//         {"title":"不能包含特殊字符"},
//         {"name": "已经存在同名"}
//     ]
// }
func adminPutTag(w http.ResponseWriter, r *http.Request) {
	t := &models.Tag{}
	if !util.ReadJSON(w, r, t) {
		return
	}

	// 检测是否为空
	errs := &util.ErrorResult{Message: "格式错误", Detail: map[string]string{}}
	if len(t.Name) == 0 {
		errs.Add("name", "不能为空")
	}
	if len(t.Title) == 0 {
		errs.Add("title", "不能为空")
	}
	if errs.HasErrors() {
		util.RenderJSON(w, http.StatusBadRequest, errs, nil)
		return
	}

	var ok bool
	t.ID, ok = util.ParamID(w, r, "id")
	if !ok {
		return
	}

	// 检测是否存在同名
	titleExists, nameExists, err := tagIsExists(t)
	if err != nil {
		logs.Error("adminPutTag:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	if titleExists {
		errs.Add("title", "与已有标签同名")
	}
	if nameExists {
		errs.Add("name", "与已有标签同名")
	}
	if errs.HasErrors() {
		util.RenderJSON(w, http.StatusBadRequest, errs, nil)
		return
	}

	if _, err := db.Update(t); err != nil {
		logs.Error("adminPutTag:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusNoContent, nil, nil)
}
예제 #8
0
파일: posts.go 프로젝트: caixw/typing
// @api get /admin/api/posts/{id} 获取某一篇文章的详细内容
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
//
// @apiSuccess 200 OK
// @apiParam id           int    id值
// @apiParam name         string 唯一名称,可以为空
// @apiParam title        string 标题
// @apiParam summary      string 文章摘要
// @apiParam content      string 文章内容
// @apiParam state        int    状态
// @apiParam order        int    排序
// @apiParam created      int    创建时间
// @apiParam modified     int    修改时间
// @apiParam template     string 所使用的模板
// @apiParam allowPing    bool   允许ping
// @apiParam allowComment bool   允许评论
// @apiParam tags         array  关联的标签。
func adminGetPost(w http.ResponseWriter, r *http.Request) {
	id, ok := util.ParamID(w, r, "id")
	if !ok {
		return
	}

	p := &models.Post{ID: id}
	if err := db.Select(p); err != nil {
		logs.Error("adminGetPost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	tags, err := getPostTags(id)
	if err != nil {
		logs.Error("adminGetPost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	obj := &struct {
		ID           int64         `json:"id"`
		Name         string        `json:"name"`
		Title        string        `json:"title"`
		Summary      string        `json:"summary"`
		Content      string        `json:"content"`
		State        int           `json:"state"`
		Order        int           `json:"order"`
		Created      int64         `json:"created"`
		Modified     int64         `json:"modified"`
		Template     string        `json:"template"`
		AllowPing    bool          `json:"allowPing"`
		AllowComment bool          `json:"allowComment"`
		Tags         []*models.Tag `json:"tags"`
	}{
		ID:           p.ID,
		Name:         p.Name,
		Title:        p.Title,
		Summary:      p.Summary,
		Content:      p.Content,
		State:        p.State,
		Order:        p.Order,
		Created:      p.Created,
		Modified:     p.Modified,
		Template:     p.Template,
		AllowPing:    p.AllowPing,
		AllowComment: p.AllowComment,
		Tags:         tags,
	}
	util.RenderJSON(w, http.StatusOK, obj, nil)
}
예제 #9
0
파일: tags.go 프로젝트: caixw/typing
// @api post /admin/api/tags 添加新标签
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
// @apiParam name        string 唯一名称
// @apiParam title       string 显示的标题
// @apiParam description string 描述信息,可以是html
// @apiExample json
// {
//     "name": "tag-1",
//     "title":"标签1",
//     "description": "<h1>desc</h1>"
// }
//
// @apiSuccess 201 created
// @apiError 400 bad request
// @apiParam message string 错误信息
// @apiParam detail  array  说细的错误信息,用于描述哪个字段有错
// @apiExample json
// {
//     "message": "格式错误",
//     "detail":[
//         {"title":"不能包含特殊字符"},
//         {"name": "已经存在同名"}
//     ]
// }
func adminPostTag(w http.ResponseWriter, r *http.Request) {
	t := &models.Tag{}
	if !util.ReadJSON(w, r, t) {
		return
	}

	errs := &util.ErrorResult{Message: "格式错误"}
	if t.ID != 0 {
		errs.Add("id", "不允许的字段")
	}
	if len(t.Title) == 0 {
		errs.Add("title", "不能为空")
	}
	if len(t.Name) == 0 {
		errs.Add("name", "不能为空")
	}
	if errs.HasErrors() {
		util.RenderJSON(w, http.StatusBadRequest, errs, nil)
		return
	}

	t.ID = 0
	titleExists, nameExists, err := tagIsExists(t)
	if err != nil {
		logs.Error("adminPostTag:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	if titleExists {
		errs.Add("title", "已有同名字体段")
	}
	if nameExists {
		errs.Add("name", "已有同名字体段")
	}
	if errs.HasErrors() {
		util.RenderJSON(w, http.StatusBadRequest, errs, nil)
		return
	}

	if _, err := db.Insert(t); err != nil {
		logs.Error("adminPostTag:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusCreated, "{}", nil)
}
예제 #10
0
파일: options.go 프로젝트: caixw/typing
// @api patch /admin/api/options/{key} 修改设置项的值
// @apiParam key string 需要修改项的key
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
// @apiParam value string 新值
// @apiExample json
// { "value": "abcdef" }
// @apiSuccess 204 no content
func adminPatchOption(w http.ResponseWriter, r *http.Request) {
	key, ok := util.ParamString(w, r, "key")
	if !ok {
		return
	}

	if _, found := app.GetOption(key); !found {
		util.RenderJSON(w, http.StatusNotFound, nil, nil)
		return
	}

	data := &struct {
		Value string `json:"value"`
	}{}
	if !util.ReadJSON(w, r, data) {
		return
	}

	if err := app.SetOption(key, data.Value, false); err != nil {
		logs.Error("adminPatchOption:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusNoContent, nil, nil)
}
예제 #11
0
파일: users.go 프로젝트: caixw/typing
// @api put /admin/api/password 理发密码
// @apiGroup admin
// @apiRequest json
// @apiHeader Authorization xxx
// @apiParam old string 旧密码
// @apiParam new string 新密码
// @apiExample json
// {
//     "old": "123",
//     "new": "456"
// }
//
// @apiSuccess 204 no content
func adminChangePassword(w http.ResponseWriter, r *http.Request) {
	l := &struct {
		Old string `json:"old"`
		New string `json:"new"`
	}{}

	if !util.ReadJSON(w, r, l) {
		return
	}

	errs := &util.ErrorResult{Message: "提交数据错误", Detail: map[string]string{}}
	if len(l.New) == 0 {
		errs.Add("new", "新密码不能为空")
	}
	if opt.Password != app.Password(l.Old) {
		errs.Add("old", "旧密码错误")
	}
	if len(errs.Detail) > 0 {
		util.RenderJSON(w, http.StatusBadRequest, errs, nil)
		return
	}

	o := &models.Option{Key: "password", Value: app.Password(l.New)}
	if _, err := db.Update(o); err != nil {
		logs.Error("adminChangePassword:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	opt.Password = o.Value
	util.RenderJSON(w, http.StatusNoContent, nil, nil)
}
예제 #12
0
파일: feed.go 프로젝트: caixw/typing
// 初始化路由项
func initRoute() error {
	m, err := web.NewModule("feed")
	if err != nil {
		return err
	}

	m.GetFunc("/sitemap.xml", func(w http.ResponseWriter, r *http.Request) {
		sitemapMutex.Lock()
		defer sitemapMutex.Unlock()

		if _, err := w.Write(sitemap.Bytes()); err != nil {
			logs.Error("feed.initRoute.route-/sitemap.xml:", err)
			w.WriteHeader(http.StatusNotFound) // 若是出错,给客户端的信息提示为404
		}
	})

	// NOTE:若修改此路由,请同时修改sitemap.xml中的相对应的.xsl路径
	m.GetFunc("/sitemap.xsl", func(w http.ResponseWriter, r *http.Request) {
		if _, err := w.Write(static.Sitemap); err != nil {
			logs.Error("feed.initRoute.route-/sitemap.xsl:", err)
			w.WriteHeader(http.StatusNotFound)
		}
	})

	m.GetFunc("/rss.xml", func(w http.ResponseWriter, r *http.Request) {
		rssMutex.Lock()
		defer rssMutex.Unlock()

		if _, err := w.Write(rss.Bytes()); err != nil {
			logs.Error("feed.initRoute.route-/rss.xml:", err)
			w.WriteHeader(http.StatusNotFound)
		}
	})

	m.GetFunc("/atom.xml", func(w http.ResponseWriter, r *http.Request) {
		atomMutex.Lock()
		defer atomMutex.Unlock()

		if _, err := w.Write(atom.Bytes()); err != nil {
			logs.Error("feed.initRoute.route-/atom.xml:", err)
			w.WriteHeader(http.StatusNotFound)
		}
	})

	return nil
}
예제 #13
0
파일: themes.go 프로젝트: caixw/typing
// 输出指定模板
func render(w http.ResponseWriter, r *http.Request, name string, data interface{}, headers map[string]string) {
	if cfg.Debug { // 调试状态下,实时加载模板
		if err := Switch(currentTheme); err != nil {
			logs.Error("front.render:", err)
		}
	}

	for key, val := range headers {
		w.Header().Set(key, val)
	}

	err := tpl.ExecuteTemplate(w, name, data)
	if err != nil {
		logs.Error("front.Render:", err)
		pageHttpStatusCode(w, r, http.StatusInternalServerError)
		return
	}
}
예제 #14
0
파일: options.go 프로젝트: caixw/typing
// @api put /admin/api/feed/sitemap 重新生成sitemap
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
//
// @apiSuccess 200 Ok
func adminPutSitemap(w http.ResponseWriter, r *http.Request) {
	err := feed.BuildSitemap()
	if err != nil {
		logs.Error(err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	util.RenderJSON(w, http.StatusOK, "{}", nil)
}
예제 #15
0
파일: post.go 프로젝트: caixw/typing
// 获取与当前文章相关的标签。
func (p *Post) Tags() []*Tag {
	sql := `SELECT t.{name} AS {Name}, t.{title} AS {Title} FROM #relationships AS r
	LEFT JOIN #tags AS t on t.{id}=r.{tagID}
	WHERE r.{postID}=?`

	rows, err := db.Query(true, sql, p.ID)
	if err != nil {
		logs.Error("front.Post.Tags:", err)
		return nil
	}
	defer rows.Close()

	tags := make([]*Tag, 0, 5)
	if _, err = fetch.Obj(&tags, rows); err != nil {
		logs.Error("front.Post.Tags:", err)
		return nil
	}
	return tags
}
예제 #16
0
파일: comments.go 프로젝트: caixw/typing
// @api delete /admin/api/comments/{id} 删除某条评论
// @apiParam id int 评论的id值
// @apiGroup admin
//
// @apiSuccess 204 no content
func adminDeleteComment(w http.ResponseWriter, r *http.Request) {
	id, ok := util.ParamID(w, r, "id")
	if !ok {
		return
	}

	c := &models.Comment{ID: id}
	if _, err := db.Delete(c); err != nil {
		logs.Error("adminDeleteComment:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if err := stats.UpdateCommentsSize(); err != nil {
		logs.Error("admin.adminDeleteComment:", err)
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusNoContent, nil, nil)
}
예제 #17
0
파일: comments.go 프로젝트: caixw/typing
// @api put /admin/api/comments/{id} 修改评论,只能修改管理员发布的评论
// @apiParam id int 需要修改的评论id
// @apiGroup admin
//
// @apiRequest json
// @apiParam content string 新的评论内容
// @apiExample json
// { "content", "content..." }
//
// @apiSuccess 200 ok
func adminPutComment(w http.ResponseWriter, r *http.Request) {
	id, ok := util.ParamID(w, r, "id")
	if !ok {
		return
	}

	c := &models.Comment{ID: id}
	cnt, err := db.Count(c)
	if err != nil {
		logs.Error("putComment:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	if cnt == 0 {
		util.RenderJSON(w, http.StatusNotFound, nil, nil)
		return
	}

	ct := &struct {
		Content string `json:"content"`
	}{}

	if !util.ReadJSON(w, r, ct) {
		return
	}

	c.Content = ct.Content

	if _, err = db.Update(c); err != nil {
		logs.Error("putComment", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if err := stats.UpdateCommentsSize(); err != nil {
		logs.Error("admin.adminPutComment:", err)
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusNoContent, nil, nil)
}
예제 #18
0
파일: users.go 프로젝트: caixw/typing
// @api post /admin/api/login 登录
// @apiGroup admin
//
// @apiRequest json
// @apiParam password string 登录密码
// @apiExample json
// { "password": "******" }
//
// @apiSuccess 201
// @apiHeader Cache-Control:no-cache
// @apiHeader Pragma:no-cache
// @apiParam token string 登录凭证;
// @apiExample json
// { "token":  "adfwerqeqaeqe313aa" }
func adminPostLogin(w http.ResponseWriter, r *http.Request) {
	inst := &struct {
		Password string `json:"password"`
	}{}
	if !util.ReadJSON(w, r, inst) {
		return
	}

	if app.Password(inst.Password) != opt.Password {
		util.RenderJSON(w, http.StatusUnauthorized, nil, nil)
		return
	}

	ret := make([]byte, 64)
	n, err := io.ReadFull(rand.Reader, ret)
	if err != nil {
		logs.Error("login:无法产生一个随机的token", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	if n == 0 {
		logs.Error("login:无法产生一个随机的token")
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	token = utils.MD5(string(ret))
	if len(token) == 0 {
		logs.Error("login:无法正确生成登录的token")
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	// 记录日志出错,仅输出错误内容,但不返回500错误。
	if err = writeLastLogs(r); err != nil {
		logs.Error("login:"******"token": token}, nil)
}
예제 #19
0
파일: tags.go 프로젝트: caixw/typing
// @api get /admin/api/tags 获取所有标签信息
// @apiGroup admin
//
// @apiRequest json
// @apiheader Authorization xxx
//
// @apiSuccess 200 OK
// @apiParam tags array 所有标签的列表
func adminGetTags(w http.ResponseWriter, r *http.Request) {
	sql := `SELECT m.{name}, m.{title}, m.{description}, m.{id},count(r.{tagID}) AS {count}
			FROM #tags AS m
			LEFT JOIN #relationships AS r ON m.{id}=r.{tagID}
			GROUP BY m.{id}`
	rows, err := db.Query(true, sql)
	if err != nil {
		logs.Error("getTags:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	maps, err := fetch.MapString(false, rows)
	rows.Close()
	if err != nil {
		logs.Error("getTags:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	util.RenderJSON(w, http.StatusOK, map[string]interface{}{"tags": maps}, nil)
}
예제 #20
0
파일: post.go 프로젝트: caixw/typing
// 评论数量
func (p *Post) CommentsSize() int {
	if size, found := stats.Posts[p.ID]; found {
		return size
	}

	c := &models.Comment{PostID: p.ID, State: models.CommentStateApproved}
	size, err := db.Count(c)
	if err != nil {
		logs.Error("front.Post.CommentsSize:", err)
		return 0
	}
	stats.Posts[p.ID] = size
	return size
}
예제 #21
0
파일: comments.go 프로젝트: caixw/typing
// @api post /admin/api/comments 提交新评论
// @apiGroup admin
//
// @apiRequest json
// @apiParam parent int 评论的父级内容
// @apiParam postID int 评论的文章
// @apiParam content string 评论的内容
//
// @apiSuccess 201 created
func adminPostComment(w http.ResponseWriter, r *http.Request) {
	c := &struct {
		Parent  int64  `json:"parent"`
		PostID  int64  `json:"postID"`
		Content string `json:"content"`
	}{}

	if !util.ReadJSON(w, r, c) {
		return
	}

	comm := &models.Comment{
		Parent:      c.Parent,
		PostID:      c.PostID,
		Content:     c.Content,
		State:       models.CommentStateApproved,
		IP:          "",
		Agent:       "",
		Created:     time.Now().Unix(),
		IsAdmin:     true,
		AuthorURL:   opt.SiteURL,
		AuthorName:  opt.ScreenName,
		AuthorEmail: opt.Email,
	}
	if _, err := db.Insert(comm); err != nil {
		logs.Error("adminPostComment:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if err := stats.UpdateCommentsSize(); err != nil {
		logs.Error("admin.adminPostComment:", err)
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusCreated, nil, nil)
}
예제 #22
0
파일: tags.go 프로젝트: caixw/typing
// @api delete /admin/api/tags/{id} 删除该id的标签,也将被从relationships表中删除。
// @apiParam id int 需要删除的标签id
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
//
// @apiSuccess 204 no content
func adminDeleteTag(w http.ResponseWriter, r *http.Request) {
	id, ok := util.ParamID(w, r, "id")
	if !ok {
		return
	}

	tx, err := db.Begin()
	if err != nil {
		logs.Error("adminDeleteMeta:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if _, err := tx.Delete(&models.Tag{ID: id}); err != nil {
		logs.Error("adminDeleteMeta:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	// 删除与之对应的关联数据。
	sql := "DELETE FROM #relationships WHERE {tagID}=?"
	if _, err := tx.Exec(true, sql, id); err != nil {
		logs.Error("adminDeleteMeta:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if err := tx.Commit(); err != nil {
		logs.Error("adminDeleteMeta:", err)
		tx.Rollback()
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusNoContent, nil, nil)
}
예제 #23
0
파일: front.go 프로젝트: caixw/typing
// @api get /api/posts/{id}/comments
// @apiQuery page  int 页码
// @apiGroup front
//
// @apiSuccess 200 OK
// @apiParam comments array 当前页的评论
func frontGetPostComments(w http.ResponseWriter, r *http.Request) {
	id, ok := util.ParamID(w, r, "id")
	if !ok {
		return
	}

	p := &models.Post{ID: id}
	if err := db.Select(p); err != nil {
		logs.Error("frontGetPostComments:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if p.State != models.PostStatePublished {
		util.RenderJSON(w, http.StatusNotFound, nil, nil)
		return
	}

	sql := db.Where("{postID}=?", id).
		And("{state}=?", models.CommentStateApproved).
		Table("#comments")

	var page int
	if page, ok = util.QueryInt(w, r, "page", 0); !ok {
		return
	}
	sql.Limit(opt.PageSize, page*opt.PageSize)
	maps, err := sql.SelectMap(true, "*")
	if err != nil {
		logs.Error("frontGetComments:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	util.RenderJSON(w, http.StatusOK, map[string]interface{}{"comments": maps}, nil)
}
예제 #24
0
파일: tags.go 프로젝트: caixw/typing
// @api get /admin/api/tags/{id} 获取指定id的标签内容
// @apiParam id int 标签的id
// @apiGroup admin
//
// @apiSuccess 200 OK
// @apiParam id          int 	标签的id
// @apiParam name        string 标签的唯一名称,可能为空
// @apiParam title       string 标签名称
// @apiParam description string 对标签的详细描述
func adminGetTag(w http.ResponseWriter, r *http.Request) {
	id, ok := util.ParamID(w, r, "id")
	if !ok {
		return
	}

	t := &models.Tag{ID: id}
	if err := db.Select(t); err != nil {
		logs.Error("adminGetTag:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	util.RenderJSON(w, http.StatusOK, t, nil)
}
예제 #25
0
파일: post.go 프로젝트: caixw/typing
// 返回文章的评论信息。
func (p *Post) Comments() []*Comment {
	sql := `SELECT {id} AS {ID}, {created} AS {Created}, {agent} AS {Agent}, {content} AS {Content},
	{isAdmin} AS {IsAdmin}, {authorName} AS {AuthorName}, {authorURL} AS {AuthorURL}, {postID} AS {PostID}
	FROM #comments
	WHERE {postID}=? AND {state}=?
	ORDER BY {created} `
	if opt.CommentOrder == app.CommentOrderDesc {
		sql += `DESC `
	}

	rows, err := db.Query(true, sql, p.ID, models.CommentStateApproved)
	if err != nil {
		logs.Error("front.Post.Comment:", err)
		return nil
	}
	defer rows.Close()

	comments := make([]*Comment, 0, opt.PageSize)
	if _, err := fetch.Obj(&comments, rows); err != nil {
		logs.Error("front.Post.Comment:", err)
		return nil
	}
	return comments
}
예제 #26
0
파일: posts.go 프로젝트: caixw/typing
// @api delete /admin/api/posts/{id} 删除文章
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
//
// @apiSuccess 204 no content
func adminDeletePost(w http.ResponseWriter, r *http.Request) {
	id, ok := util.ParamID(w, r, "id")
	if !ok {
		return
	}

	tx, err := db.Begin()
	if err != nil {
		logs.Error("deletePost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		tx.Rollback()
		return
	}

	// 删除文章
	sql := "DELETE FROM #posts WHERE {id}=?"
	if _, err := tx.Exec(true, sql, id); err != nil {
		logs.Error("deletePost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		tx.Rollback()
		return
	}

	// 删除评论
	sql = "DELETE FROM #comments WHERE {postID}=?"
	if _, err := tx.Exec(true, sql, id); err != nil {
		logs.Error("deletePost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		tx.Rollback()
		return
	}

	//删除关联数据
	sql = "DELETE FROM #relationships WHERE {postID}=?"
	if _, err := tx.Exec(true, sql, id); err != nil {
		logs.Error("deletePost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		tx.Rollback()
		return
	}

	if err := tx.Commit(); err != nil {
		tx.Rollback()
		logs.Error("deletePost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	if err := stats.UpdatePostsSize(); err != nil {
		logs.Error("admin.adminDeletePost:", err)
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusNoContent, nil, nil)
}
예제 #27
0
파일: media.go 프로젝트: caixw/typing
// @api get /admin/api/media 获取所有的文件列表
// @apiQuery parent string 上一级目录,相对于cfg.UploadDir设置项。
// @apiGroup admin
//
// @apiSuccess 200 成功获取列表
// @apiParam files array 文件列表
func adminGetMedia(w http.ResponseWriter, r *http.Request) {
	parent := r.FormValue("parent")
	if len(parent) == 0 {
		parent = "/"
	}
	if strings.Index(parent, "..") >= 0 {
		util.RenderJSON(w, http.StatusBadRequest, &util.ErrorResult{Message: "格式错误"}, nil)
		return
	}

	parent = cfg.UploadDir + parent

	fs, err := ioutil.ReadDir(parent)
	if err != nil {
		logs.Error("admin.adminGetMeida:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	type fileInfo struct {
		Name string `json:"name"`
		Type string `json:"type"`
	}
	list := make([]*fileInfo, 0, len(fs))
	for _, file := range fs {
		typ := "file"
		if file.IsDir() {
			typ = "dir"
		}
		suffix := strings.ToLower(filepath.Ext(file.Name()))
		if suffix == ".jpeg" || suffix == ".jpg" || suffix == ".png" || suffix == ".svg" || suffix == ".gif" {
			typ = "image"
		}
		list = append(list, &fileInfo{Name: file.Name(), Type: typ})
	}
	util.RenderJSON(w, http.StatusOK, map[string]interface{}{"list": list}, nil)
}
예제 #28
0
파일: front.go 프로젝트: caixw/typing
// @api post /api/posts/{id}/comments 提交新评论
// @apiGroup front
//
// @apiRequest json
// @apiParam parent      int    评论的父级内容
// @apiParam postID      int    评论的文章
// @apiParam content     string 评论的内容
// @apiParam authorName  string 评论的作者
// @apiParam authorURL   string 评论作者的网站地址,可为空
// @apiParam authorEmail string 评论作者的邮箱
//
// @apiSuccess 201 created
func frontPostPostComment(w http.ResponseWriter, r *http.Request) {
	c := &struct {
		Parent      int64  `json:"parent"`
		PostID      int64  `json:"postID"`
		Content     string `json:"content"`
		AuthorName  string `json:"authorName"`
		AuthorURL   string `json:"authorURL"`
		AuthorEmail string `json:"authorEmail"`
	}{}

	if !util.ReadJSON(w, r, c) {
		return
	}

	// 判断文章状态
	if c.PostID <= 0 {
		util.RenderJSON(w, http.StatusNotFound, nil, nil)
		return
	}

	p := &models.Post{ID: c.PostID}
	if err := db.Select(p); err != nil {
		logs.Error("forntPostPostComment:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	if (len(p.Title) == 0 && len(p.Content) == 0) || p.State != models.PostStatePublished {
		util.RenderJSON(w, http.StatusNotFound, nil, nil)
		return
	}
	if !p.AllowComment {
		util.RenderJSON(w, http.StatusMethodNotAllowed, nil, nil)
		return
	}

	// 判断提交数据的状态
	errs := &util.ErrorResult{}
	if c.Parent < 0 {
		errs.Detail["parent"] = "无效的parent"
	}
	if len(c.Content) == 0 {
		errs.Detail["content"] = "content不能为空"
	}
	if len(c.AuthorURL) > 0 && !is.URL(c.AuthorURL) {
		errs.Detail["authorURL"] = "无效的authorURL"
	}
	if !is.Email(c.AuthorEmail) {
		errs.Detail["authorEmail"] = "无效的authorEmail"
	}
	if len(c.AuthorName) == 0 {
		errs.Detail["authorName"] = "authorName不能为空"
	}

	c.AuthorName = html.EscapeString(c.AuthorName)

	// url只提取其host部分,其余的都去掉
	u, err := url.Parse(c.AuthorURL)
	if err != nil {
		logs.Error("frontPostComment:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	c.AuthorURL = u.Scheme + ":" + u.Host

	c.Content = html.EscapeString(c.Content)
	c.Content = strings.Replace(c.Content, "\n", "<br />", -1)

	comm := &models.Comment{
		PostID:      c.PostID,
		Parent:      c.Parent,
		AuthorURL:   c.AuthorURL,
		AuthorName:  c.AuthorName,
		AuthorEmail: c.AuthorEmail,
		Content:     c.Content,
		Created:     time.Now().Unix(),
		State:       models.CommentStateWaiting,
		IP:          r.RemoteAddr,
		Agent:       r.UserAgent(),
		IsAdmin:     false,
	}
	if _, err := db.Insert(comm); err != nil {
		logs.Error("frontPostComment:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	util.RenderJSON(w, http.StatusCreated, nil, nil)
}
예제 #29
0
파일: posts.go 프로젝트: caixw/typing
// @api post /admin/api/posts 新建文章
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
// @apiParam name         string 唯一名称,可以为空
// @apiParam title        string 标题
// @apiParam summary      string 文章摘要
// @apiParam content      string 文章内容
// @apiParam state        int    状态
// @apiParam order        int    排序
// @apiParam template     string 所使用的模板
// @apiParam allowPing    bool   允许ping
// @apiParam allowComment bool   允许评论
// @apiParam tags         array  关联的标签
//
// @apiSuccess 201 created
func adminPostPost(w http.ResponseWriter, r *http.Request) {
	p := &struct {
		Name         string  `json:"name"`
		Title        string  `json:"title"`
		Summary      string  `json:"summary"`
		Content      string  `json:"content"`
		State        int     `json:"state"`
		Order        int     `json:"order"`
		Template     string  `json:"template"`
		AllowPing    bool    `json:"allowPing"`
		AllowComment bool    `json:"allowComment"`
		Tags         []int64 `json:"tags"`
	}{}

	if !util.ReadJSON(w, r, p) {
		return
	}

	t := time.Now().Unix()
	pp := &models.Post{
		Name:         p.Name,
		Title:        p.Title,
		Summary:      p.Summary,
		Content:      p.Content,
		State:        p.State,
		Order:        p.Order,
		Template:     p.Template,
		AllowPing:    p.AllowPing,
		AllowComment: p.AllowComment,
		Created:      t,
		Modified:     t,
	}

	tx, err := db.Begin()
	if err != nil {
		logs.Error("adminPostPost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	// 插入文章
	result, err := tx.Insert(pp)
	if err != nil {
		tx.Rollback()
		logs.Error("adminPostPost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}
	postID, err := result.LastInsertId()
	if err != nil {
		tx.Rollback()
		logs.Error("adminPostPost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	// 插入relationship
	rs := make([]interface{}, 0, len(p.Tags))
	for _, tag := range p.Tags {
		rs = append(rs, &models.Relationship{PostID: postID, TagID: tag})
	}
	if err := tx.MultInsert(rs...); err != nil {
		tx.Rollback()
		logs.Error("adminPostPost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	// commit
	if err := tx.Commit(); err != nil {
		tx.Rollback()
		logs.Error("adminPostPost:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	lastUpdated()
	util.RenderJSON(w, http.StatusCreated, "{}", nil)
}
예제 #30
0
파일: posts.go 프로젝트: caixw/typing
// @api put /admin/api/posts/{id} 修改文章
// @apiGroup admin
//
// @apiRequest json
// @apiHeader Authorization xxx
// @apiParam name         string 唯一名称,可以为空
// @apiParam title        string 标题
// @apiParam summary      string 文章摘要
// @apiParam content      string 文章内容
// @apiParam state        int    状态
// @apiParam order        int    排序
// @apiParam template     string 所使用的模板
// @apiParam allowPing    bool   允许ping
// @apiParam allowComment bool   允许评论
// @apiParam tags         array 关联的标签
//
// @apiSuccess 200 no content
func adminPutPost(w http.ResponseWriter, r *http.Request) {
	id, ok := util.ParamID(w, r, "id")
	if !ok {
		return
	}

	p := &struct {
		Name         string  `json:"name"`
		Title        string  `json:"title"`
		Summary      string  `json:"summary"`
		Content      string  `json:"content"`
		State        int     `json:"state"`
		Order        int     `json:"order"`
		Template     string  `json:"template"`
		AllowPing    bool    `json:"allowPing"`
		AllowComment bool    `json:"allowComment"`
		Tags         []int64 `json:"tags"`
	}{}
	if !util.ReadJSON(w, r, p) {
		return
	}
	op := &models.Post{ID: id}
	if err := db.Select(op); err != nil {
		logs.Error("adminPutPost-0:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		return
	}

	pp := &models.Post{
		ID:           id,
		Name:         p.Name,
		Title:        p.Title,
		Summary:      p.Summary,
		Content:      p.Content,
		State:        p.State,
		Order:        p.Order,
		Template:     p.Template,
		AllowPing:    p.AllowPing,
		AllowComment: p.AllowComment,
		Modified:     time.Now().Unix(),
		Created:      op.Created,
	}

	// TODO 是否有必要检测标签是否真实存在

	tx, err := db.Begin()
	if err != nil {
		logs.Error("adminPutPost-1:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		tx.Rollback()
		return
	}

	// 更新文档内容
	if _, err := tx.UpdateZero(pp); err != nil {
		logs.Error("adminPutPost-2:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		tx.Rollback()
		return
	}

	// 删除旧的关联内容
	sql := "DELETE FROM #relationships WHERE {postID}=?"
	if _, err := tx.Exec(true, sql, pp.ID); err != nil {
		logs.Error("adminPutPost-3:", err)
		util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
		tx.Rollback()
		return
	}

	// 添加新的关联
	if len(p.Tags) > 0 {
		rs := make([]interface{}, 0, len(p.Tags))
		for _, tag := range p.Tags {
			rs = append(rs, &models.Relationship{TagID: tag, PostID: pp.ID})
		}
		if err := tx.MultInsert(rs...); err != nil {
			logs.Error("adminPutPost-4:", err)
			util.RenderJSON(w, http.StatusInternalServerError, nil, nil)
			tx.Rollback()
			return
		}
	}

	if err := tx.Commit(); err != nil {
		logs.Error("adminPutPost-5:", err)
		tx.Rollback()
		return
	}

	if err := stats.UpdatePostsSize(); err != nil {
		logs.Error("admin.adminPutPost:", err)
	}
	lastUpdated()
	util.RenderJSON(w, http.StatusNoContent, nil, nil)
}