|
|
|
// Copyright 2022 ROC. All rights reserved.
|
|
|
|
// Use of this source code is governed by a MIT style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package web
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"mime/multipart"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/alimy/mir/v4"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/rocboss/paopao-ce/internal/core"
|
|
|
|
"github.com/rocboss/paopao-ce/internal/core/cs"
|
|
|
|
"github.com/rocboss/paopao-ce/internal/core/ms"
|
|
|
|
"github.com/rocboss/paopao-ce/internal/model/joint"
|
|
|
|
"github.com/rocboss/paopao-ce/internal/servants/base"
|
|
|
|
"github.com/rocboss/paopao-ce/pkg/convert"
|
|
|
|
"github.com/rocboss/paopao-ce/pkg/xerror"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// 推文可见性
|
|
|
|
TweetVisitPublic TweetVisibleType = iota
|
|
|
|
TweetVisitPrivate
|
|
|
|
TweetVisitFriend
|
|
|
|
TweetVisitFollowing
|
|
|
|
TweetVisitInvalid
|
|
|
|
)
|
|
|
|
|
|
|
|
type TweetVisibleType cs.TweetVisibleType
|
|
|
|
|
|
|
|
type TweetCommentThumbsReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
TweetId int64 `json:"tweet_id" binding:"required"`
|
|
|
|
CommentId int64 `json:"comment_id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type TweetReplyThumbsReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
TweetId int64 `json:"tweet_id" binding:"required"`
|
|
|
|
CommentId int64 `json:"comment_id" binding:"required"`
|
|
|
|
ReplyId int64 `json:"reply_id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type PostContentItem struct {
|
|
|
|
Content string `json:"content" binding:"required"`
|
|
|
|
Type ms.PostContentT `json:"type" binding:"required"`
|
|
|
|
Sort int64 `json:"sort" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type CreateTweetReq struct {
|
|
|
|
BaseInfo `json:"-" binding:"-"`
|
|
|
|
Contents []*PostContentItem `json:"contents" binding:"required"`
|
|
|
|
Tags []string `json:"tags" binding:"required"`
|
|
|
|
Users []string `json:"users" binding:"required"`
|
|
|
|
AttachmentPrice int64 `json:"attachment_price"`
|
|
|
|
Visibility TweetVisibleType `json:"visibility"`
|
|
|
|
ClientIP string `json:"-" binding:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type CreateTweetResp ms.PostFormated
|
|
|
|
|
|
|
|
type DeleteTweetReq struct {
|
|
|
|
BaseInfo `json:"-" binding:"-"`
|
|
|
|
ID int64 `json:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type StarTweetReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
ID int64 `json:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type StarTweetResp struct {
|
|
|
|
Status bool `json:"status"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type CollectionTweetReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
ID int64 `json:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type CollectionTweetResp struct {
|
|
|
|
Status bool `json:"status"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type LockTweetReq struct {
|
|
|
|
BaseInfo `json:"-" binding:"-"`
|
|
|
|
ID int64 `json:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type LockTweetResp struct {
|
|
|
|
LockStatus int `json:"lock_status"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type StickTweetReq struct {
|
|
|
|
BaseInfo `json:"-" binding:"-"`
|
|
|
|
ID int64 `json:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type HighlightTweetReq struct {
|
|
|
|
BaseInfo `json:"-" binding:"-"`
|
|
|
|
ID int64 `json:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type StickTweetResp struct {
|
|
|
|
StickStatus int `json:"top_status"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type HighlightTweetResp struct {
|
|
|
|
HighlightStatus int `json:"highlight_status"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type VisibleTweetReq struct {
|
|
|
|
BaseInfo `json:"-" binding:"-"`
|
|
|
|
ID int64 `json:"id"`
|
|
|
|
Visibility TweetVisibleType `json:"visibility"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type VisibleTweetResp struct {
|
|
|
|
Visibility TweetVisibleType `json:"visibility"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type CreateCommentReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
PostID int64 `json:"post_id" binding:"required"`
|
|
|
|
Contents []*PostContentItem `json:"contents" binding:"required"`
|
|
|
|
Users []string `json:"users" binding:"required"`
|
|
|
|
ClientIP string `json:"-" binding:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type CreateCommentResp ms.Comment
|
|
|
|
|
|
|
|
type CreateCommentReplyReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
CommentID int64 `json:"comment_id" binding:"required"`
|
|
|
|
Content string `json:"content" binding:"required"`
|
|
|
|
AtUserID int64 `json:"at_user_id"`
|
|
|
|
ClientIP string `json:"-" binding:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type CreateCommentReplyResp ms.CommentReply
|
|
|
|
|
|
|
|
type DeleteCommentReq struct {
|
|
|
|
BaseInfo `json:"-" binding:"-"`
|
|
|
|
ID int64 `json:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type HighlightCommentReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
CommentId int64 `json:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type HighlightCommentResp struct {
|
|
|
|
HighlightStatus int8 `json:"highlight_status"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type DeleteCommentReplyReq struct {
|
|
|
|
BaseInfo `json:"-" binding:"-"`
|
|
|
|
ID int64 `json:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type UploadAttachmentReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
UploadType string
|
|
|
|
ContentType string
|
|
|
|
File multipart.File
|
|
|
|
FileSize int64
|
|
|
|
FileExt string
|
|
|
|
}
|
|
|
|
|
|
|
|
type UploadAttachmentResp struct {
|
|
|
|
UserID int64 `json:"user_id"`
|
|
|
|
FileSize int64 `json:"file_size"`
|
|
|
|
ImgWidth int `json:"img_width"`
|
|
|
|
ImgHeight int `json:"img_height"`
|
|
|
|
Type ms.AttachmentType `json:"type"`
|
|
|
|
Content string `json:"content"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type DownloadAttachmentPrecheckReq struct {
|
|
|
|
BaseInfo `form:"-" binding:"-"`
|
|
|
|
ContentID int64 `form:"id"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type DownloadAttachmentPrecheckResp struct {
|
|
|
|
Paid bool `json:"paid"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type DownloadAttachmentReq struct {
|
|
|
|
BaseInfo `form:"-" binding:"-"`
|
|
|
|
ContentID int64 `form:"id"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type DownloadAttachmentResp struct {
|
|
|
|
SignedURL string `json:"signed_url"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type StickTopicReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
TopicId int64 `json:"topic_id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type StickTopicResp struct {
|
|
|
|
StickStatus int8 `json:"top_status"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type FollowTopicReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
TopicId int64 `json:"topic_id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type UnfollowTopicReq struct {
|
|
|
|
SimpleInfo `json:"-" binding:"-"`
|
|
|
|
TopicId int64 `json:"topic_id" binding:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check 检查PostContentItem属性
|
|
|
|
func (p *PostContentItem) Check(acs core.AttachmentCheckService) error {
|
|
|
|
// 检查附件是否是本站资源
|
|
|
|
if p.Type == ms.ContentTypeImage || p.Type == ms.ContentTypeVideo || p.Type == ms.ContentTypeAttachment {
|
|
|
|
if err := acs.CheckAttachment(p.Content); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 检查链接是否合法
|
|
|
|
if p.Type == ms.ContentTypeLink {
|
|
|
|
if strings.Index(p.Content, "http://") != 0 && strings.Index(p.Content, "https://") != 0 {
|
|
|
|
return fmt.Errorf("链接不合法")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *UploadAttachmentReq) Bind(c *gin.Context) (xerr mir.Error) {
|
|
|
|
userId, exist := base.UserIdFrom(c)
|
|
|
|
if !exist {
|
|
|
|
return xerror.UnauthorizedAuthNotExist
|
|
|
|
}
|
|
|
|
|
|
|
|
uploadType := c.Request.FormValue("type")
|
|
|
|
file, fileHeader, err := c.Request.FormFile("file")
|
|
|
|
if err != nil {
|
|
|
|
return ErrFileUploadFailed
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if xerr != nil {
|
|
|
|
file.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if err := fileCheck(uploadType, fileHeader.Size); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
contentType := fileHeader.Header.Get("Content-Type")
|
|
|
|
fileExt, xerr := getFileExt(contentType)
|
|
|
|
if xerr != nil {
|
|
|
|
return xerr
|
|
|
|
}
|
|
|
|
r.SimpleInfo = SimpleInfo{
|
|
|
|
Uid: userId,
|
|
|
|
}
|
|
|
|
r.UploadType, r.ContentType = uploadType, contentType
|
|
|
|
r.File, r.FileSize, r.FileExt = file, fileHeader.Size, fileExt
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *DownloadAttachmentPrecheckReq) Bind(c *gin.Context) mir.Error {
|
|
|
|
user, exist := base.UserFrom(c)
|
|
|
|
if !exist {
|
|
|
|
return xerror.UnauthorizedAuthNotExist
|
|
|
|
}
|
|
|
|
r.BaseInfo = BaseInfo{
|
|
|
|
User: user,
|
|
|
|
}
|
|
|
|
r.ContentID = convert.StrTo(c.Query("id")).MustInt64()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *DownloadAttachmentReq) Bind(c *gin.Context) mir.Error {
|
|
|
|
user, exist := base.UserFrom(c)
|
|
|
|
if !exist {
|
|
|
|
return xerror.UnauthorizedAuthNotExist
|
|
|
|
}
|
|
|
|
r.BaseInfo = BaseInfo{
|
|
|
|
User: user,
|
|
|
|
}
|
|
|
|
r.ContentID = convert.StrTo(c.Query("id")).MustInt64()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *CreateTweetReq) Bind(c *gin.Context) mir.Error {
|
|
|
|
r.ClientIP = c.ClientIP()
|
|
|
|
return bindAny(c, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *CreateCommentReplyReq) Bind(c *gin.Context) mir.Error {
|
|
|
|
r.ClientIP = c.ClientIP()
|
|
|
|
return bindAny(c, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *CreateCommentReq) Bind(c *gin.Context) mir.Error {
|
|
|
|
r.ClientIP = c.ClientIP()
|
|
|
|
return bindAny(c, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *CreateTweetResp) Render(c *gin.Context) {
|
|
|
|
c.JSON(http.StatusOK, &joint.JsonResp{
|
|
|
|
Code: 0,
|
|
|
|
Msg: "success",
|
|
|
|
Data: r,
|
|
|
|
})
|
|
|
|
// 设置审核元信息,用于接下来的审核逻辑
|
|
|
|
c.Set(AuditHookCtxKey, &AuditMetaInfo{
|
|
|
|
Style: AuditStyleUserTweet,
|
|
|
|
Id: r.ID,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t TweetVisibleType) ToVisibleValue() (res cs.TweetVisibleType) {
|
|
|
|
// 原来的可见性: 0公开 1私密 2好友可见 3关注可见
|
|
|
|
// 现在的可见性: 0私密 10充电可见 20订阅可见 30保留 40保留 50好友可见 60关注可见 70保留 80保留 90公开
|
|
|
|
switch t {
|
|
|
|
case TweetVisitPublic:
|
|
|
|
res = cs.TweetVisitPublic
|
|
|
|
case TweetVisitPrivate:
|
|
|
|
res = cs.TweetVisitPrivate
|
|
|
|
case TweetVisitFriend:
|
|
|
|
res = cs.TweetVisitFriend
|
|
|
|
case TweetVisitFollowing:
|
|
|
|
res = cs.TweetVisitFollowing
|
|
|
|
default:
|
|
|
|
// TODO: 默认私密
|
|
|
|
res = cs.TweetVisitPrivate
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|