chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import "github.com/apache/answer/internal/base/constant"
|
||||
|
||||
// ActivityMsg activity message
|
||||
type ActivityMsg struct {
|
||||
UserID string
|
||||
TriggerUserID int64
|
||||
ObjectID string
|
||||
OriginalObjectID string
|
||||
ActivityTypeKey constant.ActivityTypeKey
|
||||
RevisionID string
|
||||
ExtraInfo map[string]string
|
||||
}
|
||||
|
||||
// GetObjectTimelineReq get object timeline request
|
||||
type GetObjectTimelineReq struct {
|
||||
ObjectID string `validate:"omitempty,gt=0,lte=100" form:"object_id"`
|
||||
ShowVote bool `validate:"omitempty" form:"show_vote"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
IsAdminModerator bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetObjectTimelineResp get object timeline response
|
||||
type GetObjectTimelineResp struct {
|
||||
ObjectInfo *ActObjectInfo `json:"object_info"`
|
||||
Timeline []*ActObjectTimeline `json:"timeline"`
|
||||
}
|
||||
|
||||
// ActObjectTimeline act object timeline
|
||||
type ActObjectTimeline struct {
|
||||
ActivityID string `json:"activity_id"`
|
||||
RevisionID string `json:"revision_id"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
ActivityType string `json:"activity_type"`
|
||||
Comment string `json:"comment"`
|
||||
ObjectID string `json:"object_id"`
|
||||
ObjectType string `json:"object_type"`
|
||||
Cancelled bool `json:"cancelled"`
|
||||
CancelledAt int64 `json:"cancelled_at"`
|
||||
UserInfo *UserBasicInfo `json:"user_info,omitempty"`
|
||||
}
|
||||
|
||||
// ActObjectInfo act object info
|
||||
type ActObjectInfo struct {
|
||||
Title string `json:"title"`
|
||||
ObjectType string `json:"object_type"`
|
||||
QuestionID string `json:"question_id"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
Username string `json:"username"`
|
||||
DisplayName string `json:"display_name"`
|
||||
MainTagSlugName string `json:"main_tag_slug_name"`
|
||||
}
|
||||
|
||||
// GetObjectTimelineDetailReq get object timeline detail request
|
||||
type GetObjectTimelineDetailReq struct {
|
||||
NewRevisionID string `validate:"required,gt=0,lte=100" form:"new_revision_id"`
|
||||
OldRevisionID string `validate:"required,gt=0,lte=100" form:"old_revision_id"`
|
||||
UserID string `json:"-"`
|
||||
IsAdminModerator bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetObjectTimelineDetailResp get object timeline detail response
|
||||
type GetObjectTimelineDetailResp struct {
|
||||
NewRevision *ObjectTimelineDetail `json:"new_revision"`
|
||||
OldRevision *ObjectTimelineDetail `json:"old_revision"`
|
||||
}
|
||||
|
||||
// ObjectTimelineDetail object timeline detail
|
||||
type ObjectTimelineDetail struct {
|
||||
Title string `json:"title"`
|
||||
Tags []*ObjectTimelineTag `json:"tags"`
|
||||
OriginalText string `json:"original_text"`
|
||||
SlugName string `json:"slug_name"`
|
||||
MainTagSlugName string `json:"main_tag_slug_name"`
|
||||
}
|
||||
|
||||
// ObjectTimelineTag object timeline tags
|
||||
type ObjectTimelineTag struct {
|
||||
SlugName string `json:"slug_name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
MainTagSlugName string `json:"main_tag_slug_name"`
|
||||
Recommend bool `json:"recommend"`
|
||||
Reserved bool `json:"reserved"`
|
||||
}
|
||||
|
||||
// PassReviewActivity pass review activity
|
||||
type PassReviewActivity struct {
|
||||
UserID string `json:"user_id"`
|
||||
TriggerUserID string `json:"trigger_user_id"`
|
||||
ObjectID string `json:"object_id"`
|
||||
OriginalObjectID string `json:"original_object_id"`
|
||||
RevisionID string `json:"revision_id"`
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// GetAIProviderResp get AI providers response
|
||||
type GetAIProviderResp struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
DefaultAPIHost string `json:"default_api_host"`
|
||||
}
|
||||
|
||||
// GetAIModelsResp get AI model response
|
||||
type GetAIModelsResp struct {
|
||||
Object string `json:"object"`
|
||||
Data []struct {
|
||||
Id string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int `json:"created"`
|
||||
OwnedBy string `json:"owned_by"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type GetAIModelsReq struct {
|
||||
APIHost string `json:"api_host"`
|
||||
APIKey string `json:"api_key"`
|
||||
}
|
||||
|
||||
// GetAIModelResp get AI model response
|
||||
type GetAIModelResp struct {
|
||||
Id string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int `json:"created"`
|
||||
OwnedBy string `json:"owned_by"`
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
)
|
||||
|
||||
// AIConversationListReq ai conversation list req
|
||||
type AIConversationListReq struct {
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
UserID string `validate:"omitempty" json:"-"`
|
||||
}
|
||||
|
||||
// AIConversationListItem ai conversation list item
|
||||
type AIConversationListItem struct {
|
||||
ConversationID string `json:"conversation_id"`
|
||||
Topic string `json:"topic"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
// AIConversationDetailReq ai conversation detail req
|
||||
type AIConversationDetailReq struct {
|
||||
ConversationID string `validate:"required" form:"conversation_id" json:"conversation_id"`
|
||||
UserID string `validate:"omitempty" json:"-"`
|
||||
}
|
||||
|
||||
// AIConversationRecord ai conversation record
|
||||
type AIConversationRecord struct {
|
||||
ChatCompletionID string `json:"chat_completion_id"`
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
ReasoningContent string `json:"reasoning_content,omitempty"`
|
||||
Helpful int `json:"helpful"`
|
||||
Unhelpful int `json:"unhelpful"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
// AIConversationDetailResp ai conversation detail resp
|
||||
type AIConversationDetailResp struct {
|
||||
ConversationID string `json:"conversation_id"`
|
||||
Topic string `json:"topic"`
|
||||
Records []*AIConversationRecord `json:"records"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
// AIConversationVoteReq ai conversation vote req
|
||||
type AIConversationVoteReq struct {
|
||||
ChatCompletionID string `validate:"required" json:"chat_completion_id"`
|
||||
VoteType string `validate:"required,oneof=helpful unhelpful" json:"vote_type"`
|
||||
Cancel bool `validate:"omitempty" json:"cancel"`
|
||||
UserID string `validate:"omitempty" json:"-"`
|
||||
}
|
||||
|
||||
// AIConversationAdminListReq ai conversation admin list req
|
||||
type AIConversationAdminListReq struct {
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
}
|
||||
|
||||
// AIConversationAdminListItem ai conversation admin list item
|
||||
type AIConversationAdminListItem struct {
|
||||
ID string `json:"id"`
|
||||
Topic string `json:"topic"`
|
||||
UserInfo AIConversationUserInfo `json:"user_info"`
|
||||
HelpfulCount int64 `json:"helpful_count"`
|
||||
UnhelpfulCount int64 `json:"unhelpful_count"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
// AIConversationUserInfo ai conversation user info
|
||||
type AIConversationUserInfo struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Rank int `json:"rank"`
|
||||
}
|
||||
|
||||
// AIConversationAdminDetailReq ai conversation admin detail req
|
||||
type AIConversationAdminDetailReq struct {
|
||||
ConversationID string `validate:"required" form:"conversation_id" json:"conversation_id"`
|
||||
}
|
||||
|
||||
// AIConversationAdminDetailResp ai conversation admin detail resp
|
||||
type AIConversationAdminDetailResp struct {
|
||||
ConversationID string `json:"conversation_id"`
|
||||
Topic string `json:"topic"`
|
||||
UserInfo AIConversationUserInfo `json:"user_info"`
|
||||
Records []AIConversationRecord `json:"records"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
// AIConversationAdminDeleteReq admin delete ai
|
||||
type AIConversationAdminDeleteReq struct {
|
||||
ConversationID string `validate:"required" json:"conversation_id"`
|
||||
}
|
||||
|
||||
func (req *AIConversationDetailReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (req *AIConversationVoteReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// AcceptAnswerOperationInfo accept answer operation info
|
||||
type AcceptAnswerOperationInfo struct {
|
||||
TriggerUserID string
|
||||
QuestionObjectID string
|
||||
QuestionUserID string
|
||||
AnswerObjectID string
|
||||
AnswerUserID string
|
||||
|
||||
// vote activity info
|
||||
Activities []*AcceptAnswerActivity
|
||||
}
|
||||
|
||||
// AcceptAnswerActivity accept answer activity
|
||||
type AcceptAnswerActivity struct {
|
||||
ActivityType int
|
||||
ActivityUserID string
|
||||
TriggerUserID string
|
||||
OriginalObjectID string
|
||||
Rank int
|
||||
}
|
||||
|
||||
func (v *AcceptAnswerActivity) HasRank() int {
|
||||
if v.Rank != 0 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (a *AcceptAnswerOperationInfo) GetUserIDs() (userIDs []string) {
|
||||
for _, act := range a.Activities {
|
||||
userIDs = append(userIDs, act.ActivityUserID)
|
||||
}
|
||||
return userIDs
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/apache/answer/pkg/converter"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// RemoveAnswerReq delete answer request
|
||||
type RemoveAnswerReq struct {
|
||||
ID string `validate:"required" json:"id"`
|
||||
UserID string `json:"-"`
|
||||
CanDelete bool `json:"-"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
// RecoverAnswerReq recover answer request
|
||||
type RecoverAnswerReq struct {
|
||||
AnswerID string `validate:"required" json:"answer_id"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
const (
|
||||
AnswerAcceptedFailed = 1
|
||||
AnswerAcceptedEnable = 2
|
||||
)
|
||||
|
||||
type AnswerAddReq struct {
|
||||
QuestionID string `json:"question_id"`
|
||||
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
|
||||
HTML string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
CanEdit bool `json:"-"`
|
||||
CanDelete bool `json:"-"`
|
||||
CanRecover bool `json:"-"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
IP string `json:"-"`
|
||||
UserAgent string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *AnswerAddReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.HTML = converter.Markdown2HTML(req.Content)
|
||||
if req.HTML == "" {
|
||||
return append(errFields, &validator.FormErrorField{
|
||||
ErrorField: "content",
|
||||
ErrorMsg: reason.AnswerContentCannotEmpty,
|
||||
}), errors.BadRequest(reason.AnswerContentCannotEmpty)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type GetAnswerInfoResp struct {
|
||||
Info *AnswerInfo `json:"info"`
|
||||
Question *QuestionInfoResp `json:"question"`
|
||||
}
|
||||
|
||||
type AnswerUpdateReq struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"`
|
||||
HTML string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
NoNeedReview bool `json:"-"`
|
||||
CanEdit bool `json:"-"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
func (req *AnswerUpdateReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.HTML = converter.Markdown2HTML(req.Content)
|
||||
if req.HTML == "" {
|
||||
return append(errFields, &validator.FormErrorField{
|
||||
ErrorField: "content",
|
||||
ErrorMsg: reason.AnswerContentCannotEmpty,
|
||||
}), errors.BadRequest(reason.AnswerContentCannotEmpty)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// AnswerUpdateResp answer update resp
|
||||
type AnswerUpdateResp struct {
|
||||
WaitForReview bool `json:"wait_for_review"`
|
||||
}
|
||||
|
||||
type AnswerListReq struct {
|
||||
QuestionID string `json:"question_id" form:"question_id"`
|
||||
Order string `json:"order" form:"order"`
|
||||
Page int `json:"page" form:"page"`
|
||||
PageSize int `json:"page_size" form:"page_size"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
IsAdminModerator bool `json:"-"`
|
||||
CanEdit bool `json:"-"`
|
||||
CanDelete bool `json:"-"`
|
||||
CanRecover bool `json:"-"`
|
||||
}
|
||||
|
||||
type AnswerInfo struct {
|
||||
ID string `json:"id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
Content string `json:"content"`
|
||||
HTML string `json:"html"`
|
||||
CreateTime int64 `json:"create_time"`
|
||||
UpdateTime int64 `json:"update_time"`
|
||||
Accepted int `json:"accepted"`
|
||||
UserID string `json:"-"`
|
||||
UpdateUserID string `json:"-"`
|
||||
UserInfo *UserBasicInfo `json:"user_info,omitempty"`
|
||||
UpdateUserInfo *UserBasicInfo `json:"update_user_info,omitempty"`
|
||||
Collected bool `json:"collected"`
|
||||
VoteStatus string `json:"vote_status"`
|
||||
VoteCount int `json:"vote_count"`
|
||||
QuestionInfo *QuestionInfoResp `json:"question_info,omitempty"`
|
||||
Status int `json:"status"`
|
||||
|
||||
// MemberActions
|
||||
MemberActions []*PermissionMemberAction `json:"member_actions"`
|
||||
}
|
||||
|
||||
type AdminAnswerInfo struct {
|
||||
ID string `json:"id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
Description string `json:"description"`
|
||||
CreateTime int64 `json:"create_time"`
|
||||
UpdateTime int64 `json:"update_time"`
|
||||
Accepted int `json:"accepted"`
|
||||
UserID string `json:"-"`
|
||||
UpdateUserID string `json:"-"`
|
||||
UserInfo *UserBasicInfo `json:"user_info"`
|
||||
VoteCount int `json:"vote_count"`
|
||||
QuestionInfo struct {
|
||||
Title string `json:"title"`
|
||||
} `json:"question_info"`
|
||||
}
|
||||
|
||||
type AcceptAnswerReq struct {
|
||||
QuestionID string `validate:"required,gt=0,lte=30" json:"question_id"`
|
||||
AnswerID string `validate:"omitempty" json:"answer_id"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *AcceptAnswerReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
if len(req.AnswerID) == 0 {
|
||||
req.AnswerID = "0"
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type AdminUpdateAnswerStatusReq struct {
|
||||
AnswerID string `validate:"required" json:"answer_id"`
|
||||
Status string `validate:"required,oneof=available deleted" json:"status"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// GetAPIKeyReq get api key request
|
||||
type GetAPIKeyReq struct {
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// GetAPIKeyResp get api keys response
|
||||
type GetAPIKeyResp struct {
|
||||
ID int `json:"id"`
|
||||
AccessKey string `json:"access_key"`
|
||||
Description string `json:"description"`
|
||||
Scope string `json:"scope"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
LastUsedAt int64 `json:"last_used_at"`
|
||||
}
|
||||
|
||||
// AddAPIKeyReq add api key request
|
||||
type AddAPIKeyReq struct {
|
||||
Description string `validate:"required,notblank,lte=150" json:"description"`
|
||||
Scope string `validate:"required,oneof=read-only global" json:"scope"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// AddAPIKeyResp add api key response
|
||||
type AddAPIKeyResp struct {
|
||||
AccessKey string `json:"access_key"`
|
||||
}
|
||||
|
||||
// UpdateAPIKeyReq update api key request
|
||||
type UpdateAPIKeyReq struct {
|
||||
ID int `validate:"required" json:"id"`
|
||||
Description string `validate:"required,notblank,lte=150" json:"description"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// DeleteAPIKeyReq delete api key request
|
||||
type DeleteAPIKeyReq struct {
|
||||
ID int `json:"id"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/internal/base/handler"
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/apache/answer/internal/base/translator"
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/apache/answer/internal/entity"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// UpdateUserStatusReq update user request
|
||||
type UpdateUserStatusReq struct {
|
||||
UserID string `validate:"required" json:"user_id"`
|
||||
Status string `validate:"required,oneof=normal suspended deleted inactive" json:"status" enums:"normal,suspended,deleted,inactive"`
|
||||
SuspendDuration string `validate:"omitempty,oneof=24h 48h 72h 7d 14d 1m 2m 3m 6m 1y forever" json:"suspend_duration"`
|
||||
RemoveAllContent bool `validate:"omitempty" json:"remove_all_content"`
|
||||
LoginUserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (r *UpdateUserStatusReq) IsNormal() bool { return r.Status == constant.UserNormal }
|
||||
func (r *UpdateUserStatusReq) IsSuspended() bool { return r.Status == constant.UserSuspended }
|
||||
func (r *UpdateUserStatusReq) IsDeleted() bool { return r.Status == constant.UserDeleted }
|
||||
func (r *UpdateUserStatusReq) IsInactive() bool { return r.Status == constant.UserInactive }
|
||||
|
||||
// GetSuspendedUntil calculates the suspended until time based on duration
|
||||
func (r *UpdateUserStatusReq) GetSuspendedUntil() time.Time {
|
||||
if !r.IsSuspended() || r.SuspendDuration == "" || r.SuspendDuration == "forever" {
|
||||
return entity.PermanentSuspensionTime // permanent suspension
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
switch r.SuspendDuration {
|
||||
case "24h":
|
||||
return now.Add(24 * time.Hour)
|
||||
case "48h":
|
||||
return now.Add(48 * time.Hour)
|
||||
case "72h":
|
||||
return now.Add(72 * time.Hour)
|
||||
case "7d":
|
||||
return now.Add(7 * 24 * time.Hour)
|
||||
case "14d":
|
||||
return now.Add(14 * 24 * time.Hour)
|
||||
case "1m":
|
||||
return now.AddDate(0, 1, 0)
|
||||
case "2m":
|
||||
return now.AddDate(0, 2, 0)
|
||||
case "3m":
|
||||
return now.AddDate(0, 3, 0)
|
||||
case "6m":
|
||||
return now.AddDate(0, 6, 0)
|
||||
case "1y":
|
||||
return now.AddDate(1, 0, 0)
|
||||
default:
|
||||
return entity.PermanentSuspensionTime // fallback to permanent
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserPageReq get user list page request
|
||||
type GetUserPageReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// email
|
||||
Query string `validate:"omitempty,gt=0,lte=100" form:"query"`
|
||||
// user status
|
||||
Status string `validate:"omitempty,oneof=normal suspended deleted inactive" form:"status"`
|
||||
// staff, if staff is true means query admin or moderator
|
||||
Staff bool `validate:"omitempty" form:"staff"`
|
||||
}
|
||||
|
||||
func (r *GetUserPageReq) IsSuspended() bool { return r.Status == constant.UserSuspended }
|
||||
func (r *GetUserPageReq) IsDeleted() bool { return r.Status == constant.UserDeleted }
|
||||
func (r *GetUserPageReq) IsInactive() bool { return r.Status == constant.UserInactive }
|
||||
|
||||
// GetUserPageResp get user response
|
||||
type GetUserPageResp struct {
|
||||
// user id
|
||||
UserID string `json:"user_id"`
|
||||
// create time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
// delete time
|
||||
DeletedAt int64 `json:"deleted_at"`
|
||||
// suspended time
|
||||
SuspendedAt int64 `json:"suspended_at"`
|
||||
// suspended until time
|
||||
SuspendedUntil int64 `json:"suspended_until"`
|
||||
// username
|
||||
Username string `json:"username"`
|
||||
// email
|
||||
EMail string `json:"e_mail"`
|
||||
// rank
|
||||
Rank int `json:"rank"`
|
||||
// user status(normal,suspended,deleted,inactive)
|
||||
Status string `json:"status"`
|
||||
// display name
|
||||
DisplayName string `json:"display_name"`
|
||||
// avatar
|
||||
Avatar string `json:"avatar"`
|
||||
// role id
|
||||
RoleID int `json:"role_id"`
|
||||
// role name
|
||||
RoleName string `json:"role_name"`
|
||||
}
|
||||
|
||||
// GetUserInfoReq get user request
|
||||
type GetUserInfoReq struct {
|
||||
UserID string `validate:"required" json:"user_id"`
|
||||
}
|
||||
|
||||
// GetUserInfoResp get user response
|
||||
type GetUserInfoResp struct {
|
||||
// suspended until
|
||||
SuspendedUntil time.Time `json:"suspended_until"`
|
||||
}
|
||||
|
||||
// UpdateUserRoleReq update user role request
|
||||
type UpdateUserRoleReq struct {
|
||||
// user id
|
||||
UserID string `validate:"required" json:"user_id"`
|
||||
// role id
|
||||
RoleID int `validate:"required" json:"role_id"`
|
||||
// login user id
|
||||
LoginUserID string `json:"-"`
|
||||
}
|
||||
|
||||
// EditUserProfileReq edit user profile request
|
||||
type EditUserProfileReq struct {
|
||||
UserID string `validate:"required" json:"user_id"`
|
||||
DisplayName string `validate:"required,gte=2,lte=30" json:"display_name"`
|
||||
Username string `validate:"omitempty,gte=2,lte=30" json:"username"`
|
||||
Email string `validate:"required,email,gt=0,lte=500" json:"email"`
|
||||
LoginUserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
// AddUserReq add user request
|
||||
type AddUserReq struct {
|
||||
DisplayName string `validate:"required,gte=2,lte=30" json:"display_name"`
|
||||
Email string `validate:"required,email,gt=0,lte=500" json:"email"`
|
||||
Password string `validate:"required,gte=8,lte=32" json:"password"`
|
||||
LoginUserID string `json:"-"`
|
||||
}
|
||||
|
||||
// AddUsersReq add users request
|
||||
type AddUsersReq struct {
|
||||
// users info line by line
|
||||
UsersStr string `json:"users"`
|
||||
Users []*AddUserReq `json:"-"`
|
||||
}
|
||||
|
||||
// DeletePermanentlyReq delete permanently request
|
||||
type DeletePermanentlyReq struct {
|
||||
Type string `validate:"required,oneof=users questions answers" json:"type"`
|
||||
}
|
||||
|
||||
type AddUsersErrorData struct {
|
||||
// optional. error field name.
|
||||
Field string `json:"field"`
|
||||
// must. error line number.
|
||||
Line int `json:"line"`
|
||||
// must. error content.
|
||||
Content string `json:"content"`
|
||||
// optional. error message.
|
||||
ExtraMessage string `json:"extra_message"`
|
||||
}
|
||||
|
||||
func (e *AddUsersErrorData) GetErrField(ctx context.Context) (errFields []*validator.FormErrorField) {
|
||||
return append([]*validator.FormErrorField{}, &validator.FormErrorField{
|
||||
ErrorField: "users",
|
||||
ErrorMsg: translator.TrWithData(handler.GetLangByCtx(ctx), reason.AddBulkUsersFormatError, e),
|
||||
})
|
||||
}
|
||||
|
||||
func (req *AddUsersReq) ParseUsers(ctx context.Context) (errFields []*validator.FormErrorField, err error) {
|
||||
req.UsersStr = strings.TrimSpace(req.UsersStr)
|
||||
lines := strings.Split(req.UsersStr, "\n")
|
||||
req.Users = make([]*AddUserReq, 0)
|
||||
for i, line := range lines {
|
||||
arr := strings.Split(line, ",")
|
||||
if len(arr) != 3 {
|
||||
errFields = append([]*validator.FormErrorField{}, &validator.FormErrorField{
|
||||
ErrorField: "users",
|
||||
ErrorMsg: translator.TrWithData(handler.GetLangByCtx(ctx), reason.AddBulkUsersFormatError,
|
||||
&AddUsersErrorData{
|
||||
Line: i + 1,
|
||||
Content: line,
|
||||
}),
|
||||
})
|
||||
return errFields, errors.BadRequest(reason.RequestFormatError)
|
||||
}
|
||||
req.Users = append(req.Users, &AddUserReq{
|
||||
DisplayName: strings.TrimSpace(arr[0]),
|
||||
Email: strings.TrimSpace(arr[1]),
|
||||
Password: strings.TrimSpace(arr[2]),
|
||||
})
|
||||
}
|
||||
|
||||
// check users amount
|
||||
if len(req.Users) == 0 || len(req.Users) > constant.DefaultBulkUser {
|
||||
errFields = append([]*validator.FormErrorField{}, &validator.FormErrorField{
|
||||
ErrorField: "users",
|
||||
ErrorMsg: translator.TrWithData(handler.GetLangByCtx(ctx), reason.AddBulkUsersAmountError,
|
||||
map[string]int{
|
||||
"MaxAmount": constant.DefaultBulkUser,
|
||||
}),
|
||||
})
|
||||
return errFields, errors.BadRequest(reason.RequestFormatError)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// UpdateUserPasswordReq update user password request
|
||||
type UpdateUserPasswordReq struct {
|
||||
UserID string `validate:"required" json:"user_id"`
|
||||
Password string `validate:"required,gte=8,lte=32" json:"password"`
|
||||
LoginUserID string `json:"-"`
|
||||
}
|
||||
|
||||
// GetUserActivationReq get user activation
|
||||
type GetUserActivationReq struct {
|
||||
UserID string `validate:"required" form:"user_id"`
|
||||
}
|
||||
|
||||
// GetUserActivationResp get user activation
|
||||
type GetUserActivationResp struct {
|
||||
ActivationURL string `json:"activation_url"`
|
||||
}
|
||||
|
||||
// SendUserActivationReq send user activation
|
||||
type SendUserActivationReq struct {
|
||||
UserID string `validate:"required" json:"user_id"`
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import "github.com/apache/answer/internal/entity"
|
||||
|
||||
const (
|
||||
BadgeStatusActive BadgeStatus = "active"
|
||||
BadgeStatusInactive BadgeStatus = "inactive"
|
||||
)
|
||||
|
||||
type BadgeStatus string
|
||||
|
||||
var BadgeStatusMap = map[int8]BadgeStatus{
|
||||
entity.BadgeStatusActive: BadgeStatusActive,
|
||||
entity.BadgeStatusInactive: BadgeStatusInactive,
|
||||
}
|
||||
|
||||
var BadgeStatusEMap = map[BadgeStatus]int8{
|
||||
BadgeStatusActive: entity.BadgeStatusActive,
|
||||
BadgeStatusInactive: entity.BadgeStatusInactive,
|
||||
}
|
||||
|
||||
// BadgeListInfo get badge list response
|
||||
type BadgeListInfo struct {
|
||||
// badge id
|
||||
ID string `json:"id" `
|
||||
// badge name
|
||||
Name string `json:"name" `
|
||||
// badge icon
|
||||
Icon string `json:"icon" `
|
||||
// badge award count
|
||||
AwardCount int `json:"award_count" `
|
||||
// badge earned count
|
||||
EarnedCount int64 `json:"earned_count" `
|
||||
// badge level
|
||||
Level entity.BadgeLevel `json:"level" `
|
||||
}
|
||||
|
||||
type GetBadgeListResp struct {
|
||||
// badge list info
|
||||
Badges []*BadgeListInfo `json:"badges" `
|
||||
// badge group name
|
||||
GroupName string `json:"group_name" `
|
||||
}
|
||||
|
||||
type UpdateBadgeStatusReq struct {
|
||||
// badge id
|
||||
ID string `validate:"required" json:"id"`
|
||||
// badge status
|
||||
Status BadgeStatus `validate:"required" json:"status"`
|
||||
}
|
||||
|
||||
type GetBadgeListPagedReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// badge status
|
||||
Status BadgeStatus `validate:"omitempty" form:"status"`
|
||||
// query condition
|
||||
Query string `validate:"omitempty" form:"q"`
|
||||
}
|
||||
|
||||
type GetBadgeListPagedResp struct {
|
||||
// badge id
|
||||
ID string `json:"id" `
|
||||
// badge name
|
||||
Name string `json:"name" `
|
||||
// badge description
|
||||
Description string `json:"description" `
|
||||
// badge icon
|
||||
Icon string `json:"icon" `
|
||||
// badge award count
|
||||
AwardCount int `json:"award_count" `
|
||||
// badge earned count
|
||||
Earned bool `json:"earned" `
|
||||
// badge level
|
||||
Level entity.BadgeLevel `json:"level" `
|
||||
// badge group name
|
||||
GroupName string `json:"group_name" `
|
||||
// badge status
|
||||
Status BadgeStatus `json:"status"`
|
||||
}
|
||||
|
||||
type GetBadgeInfoResp struct {
|
||||
// badge id
|
||||
ID string `json:"id" `
|
||||
// badge name
|
||||
Name string `json:"name" `
|
||||
// badge description
|
||||
Description string `json:"description" `
|
||||
// badge icon
|
||||
Icon string `json:"icon" `
|
||||
// badge award count
|
||||
AwardCount int `json:"award_count" `
|
||||
// badge earned count
|
||||
EarnedCount int64 `json:"earned_count" `
|
||||
// badge is single or multiple
|
||||
IsSingle bool `json:"is_single" `
|
||||
// badge level
|
||||
Level entity.BadgeLevel `json:"level" `
|
||||
}
|
||||
|
||||
type GetBadgeAwardWithPageReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// badge id
|
||||
BadgeID string `validate:"required" form:"badge_id"`
|
||||
// username
|
||||
Username string `validate:"omitempty,gt=0,lte=100" form:"username"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type GetBadgeAwardWithPageResp struct {
|
||||
// created time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
// object id
|
||||
ObjectID string `json:"object_id"`
|
||||
// question id
|
||||
QuestionID string `json:"question_id"`
|
||||
// answer id
|
||||
AnswerID string `json:"answer_id"`
|
||||
// comment id
|
||||
CommentID string `json:"comment_id"`
|
||||
// object type
|
||||
ObjectType string `json:"object_type" enums:"question,answer,comment"`
|
||||
// url title
|
||||
UrlTitle string `json:"url_title"`
|
||||
// author user info
|
||||
AuthorUserInfo UserBasicInfo `json:"author_user_info"`
|
||||
}
|
||||
|
||||
type GetUserBadgeAwardListReq struct {
|
||||
// username
|
||||
Username string `validate:"required,gt=0,lte=100" form:"username"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
Limit int `json:"-"`
|
||||
}
|
||||
|
||||
type GetUserBadgeAwardListResp struct {
|
||||
// badge id
|
||||
ID string `json:"id" `
|
||||
// badge name
|
||||
Name string `json:"name" `
|
||||
// badge icon
|
||||
Icon string `json:"icon" `
|
||||
// badge award count
|
||||
EarnedCount int64 `json:"earned_count" `
|
||||
// badge level
|
||||
Level entity.BadgeLevel `json:"level" `
|
||||
}
|
||||
|
||||
type BadgeTplData struct {
|
||||
ProfileURL string
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
CGDefault = 1
|
||||
CGDIY = 2
|
||||
)
|
||||
|
||||
// CollectionSwitchReq switch collection request
|
||||
type CollectionSwitchReq struct {
|
||||
ObjectID string `validate:"required" json:"object_id"`
|
||||
GroupID string `validate:"required" json:"group_id"`
|
||||
Bookmark bool `validate:"omitempty" json:"bookmark"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// CollectionSwitchResp switch collection response
|
||||
type CollectionSwitchResp struct {
|
||||
ObjectCollectionCount int64 `json:"object_collection_count"`
|
||||
}
|
||||
|
||||
// AddCollectionGroupReq add collection group request
|
||||
type AddCollectionGroupReq struct {
|
||||
//
|
||||
UserID int64 `validate:"required" comment:"" json:"user_id"`
|
||||
// the collection group name
|
||||
Name string `validate:"required,gt=0,lte=50" comment:"the collection group name" json:"name"`
|
||||
// mark this group is default, default 1
|
||||
DefaultGroup int `validate:"required" comment:"mark this group is default, default 1" json:"default_group"`
|
||||
//
|
||||
CreateTime time.Time `validate:"required" comment:"" json:"create_time"`
|
||||
//
|
||||
UpdateTime time.Time `validate:"required" comment:"" json:"update_time"`
|
||||
}
|
||||
|
||||
// UpdateCollectionGroupReq update collection group request
|
||||
type UpdateCollectionGroupReq struct {
|
||||
//
|
||||
ID int64 `validate:"required" comment:"" json:"id"`
|
||||
//
|
||||
UserID int64 `validate:"omitempty" comment:"" json:"user_id"`
|
||||
// the collection group name
|
||||
Name string `validate:"omitempty,gt=0,lte=50" comment:"the collection group name" json:"name"`
|
||||
// mark this group is default, default 1
|
||||
DefaultGroup int `validate:"omitempty" comment:"mark this group is default, default 1" json:"default_group"`
|
||||
//
|
||||
CreateTime time.Time `validate:"omitempty" comment:"" json:"create_time"`
|
||||
//
|
||||
UpdateTime time.Time `validate:"omitempty" comment:"" json:"update_time"`
|
||||
}
|
||||
|
||||
// GetCollectionGroupResp get collection group response
|
||||
type GetCollectionGroupResp struct {
|
||||
//
|
||||
ID int64 `json:"id"`
|
||||
//
|
||||
UserID int64 `json:"user_id"`
|
||||
// the collection group name
|
||||
Name string `json:"name"`
|
||||
// mark this group is default, default 1
|
||||
DefaultGroup int `json:"default_group"`
|
||||
//
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
//
|
||||
UpdateTime time.Time `json:"update_time"`
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/apache/answer/internal/entity"
|
||||
"github.com/apache/answer/pkg/converter"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// AddCommentReq add comment request
|
||||
type AddCommentReq struct {
|
||||
// object id
|
||||
ObjectID string `validate:"required" json:"object_id"`
|
||||
// reply comment id
|
||||
ReplyCommentID string `validate:"omitempty" json:"reply_comment_id"`
|
||||
// original comment content
|
||||
OriginalText string `validate:"required,notblank,gte=2,lte=600" json:"original_text"`
|
||||
// parsed comment content
|
||||
ParsedText string `json:"-"`
|
||||
// @ user id list
|
||||
MentionUsernameList []string `validate:"omitempty" json:"mention_username_list"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
// whether user can add it
|
||||
CanAdd bool `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
|
||||
IP string `json:"-"`
|
||||
UserAgent string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *AddCommentReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.ParsedText = converter.Markdown2HTML(req.OriginalText)
|
||||
if req.ParsedText == "" {
|
||||
return append(errFields, &validator.FormErrorField{
|
||||
ErrorField: "original_text",
|
||||
ErrorMsg: reason.CommentContentCannotEmpty,
|
||||
}), errors.BadRequest(reason.CommentContentCannotEmpty)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// RemoveCommentReq remove comment
|
||||
type RemoveCommentReq struct {
|
||||
// comment id
|
||||
CommentID string `validate:"required" json:"comment_id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
// UpdateCommentReq update comment request
|
||||
type UpdateCommentReq struct {
|
||||
// comment id
|
||||
CommentID string `validate:"required" json:"comment_id"`
|
||||
// original comment content
|
||||
OriginalText string `validate:"required,notblank,gte=2,lte=600" json:"original_text"`
|
||||
// parsed comment content
|
||||
ParsedText string `json:"-"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
|
||||
// whether user can delete it
|
||||
CaptchaID string `json:"captcha_id"` // captcha_id
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
func (req *UpdateCommentReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.ParsedText = converter.Markdown2HTML(req.OriginalText)
|
||||
if req.ParsedText == "" {
|
||||
return append(errFields, &validator.FormErrorField{
|
||||
ErrorField: "original_text",
|
||||
ErrorMsg: reason.CommentContentCannotEmpty,
|
||||
}), errors.BadRequest(reason.CommentContentCannotEmpty)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type UpdateCommentResp struct {
|
||||
// comment id
|
||||
CommentID string `json:"comment_id"`
|
||||
// original comment content
|
||||
OriginalText string `json:"original_text"`
|
||||
// parsed comment content
|
||||
ParsedText string `json:"parsed_text"`
|
||||
}
|
||||
|
||||
// GetCommentListReq get comment list all request
|
||||
type GetCommentListReq struct {
|
||||
// user id
|
||||
UserID int64 `validate:"omitempty" comment:"user id" form:"user_id"`
|
||||
// reply user id
|
||||
ReplyUserID int64 `validate:"omitempty" comment:"reply user id" form:"reply_user_id"`
|
||||
// reply comment id
|
||||
ReplyCommentID int64 `validate:"omitempty" comment:"reply comment id" form:"reply_comment_id"`
|
||||
// object id
|
||||
ObjectID int64 `validate:"omitempty" comment:"object id" form:"object_id"`
|
||||
// user vote amount
|
||||
VoteCount int `validate:"omitempty" comment:"user vote amount" form:"vote_count"`
|
||||
// comment status(available: 0; deleted: 10)
|
||||
Status int `validate:"omitempty" comment:"comment status(available: 0; deleted: 10)" form:"status"`
|
||||
// original comment content
|
||||
OriginalText string `validate:"omitempty" comment:"original comment content" form:"original_text"`
|
||||
// parsed comment content
|
||||
ParsedText string `validate:"omitempty" comment:"parsed comment content" form:"parsed_text"`
|
||||
}
|
||||
|
||||
// GetCommentWithPageReq get comment list page request
|
||||
type GetCommentWithPageReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// object id
|
||||
ObjectID string `validate:"required" form:"object_id"`
|
||||
// comment id
|
||||
CommentID string `validate:"omitempty" form:"comment_id"`
|
||||
// query condition
|
||||
QueryCond string `validate:"omitempty,oneof=vote created_at" form:"query_cond"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
IsAdminModerator bool `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetCommentReq get comment list page request
|
||||
type GetCommentReq struct {
|
||||
// object id
|
||||
ID string `validate:"required" form:"id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
IsAdminModerator bool `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetCommentResp comment response
|
||||
type GetCommentResp struct {
|
||||
// comment id
|
||||
CommentID string `json:"comment_id"`
|
||||
// create time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
|
||||
// object id
|
||||
ObjectID string `json:"object_id"`
|
||||
// user vote amount
|
||||
VoteCount int `json:"vote_count"`
|
||||
// current user if already vote this comment
|
||||
IsVote bool `json:"is_vote"`
|
||||
// original comment content
|
||||
OriginalText string `json:"original_text"`
|
||||
// parsed comment content
|
||||
ParsedText string `json:"parsed_text"`
|
||||
|
||||
// user id
|
||||
UserID string `json:"user_id"`
|
||||
// username
|
||||
Username string `json:"username"`
|
||||
// user display name
|
||||
UserDisplayName string `json:"user_display_name"`
|
||||
// user avatar
|
||||
UserAvatar string `json:"user_avatar"`
|
||||
// user status
|
||||
UserStatus string `json:"user_status"`
|
||||
|
||||
// reply user id
|
||||
ReplyUserID string `json:"reply_user_id"`
|
||||
// reply user username
|
||||
ReplyUsername string `json:"reply_username"`
|
||||
// reply user display name
|
||||
ReplyUserDisplayName string `json:"reply_user_display_name"`
|
||||
// reply comment id
|
||||
ReplyCommentID string `json:"reply_comment_id"`
|
||||
// reply user status
|
||||
ReplyUserStatus string `json:"reply_user_status"`
|
||||
|
||||
// MemberActions
|
||||
MemberActions []*PermissionMemberAction `json:"member_actions"`
|
||||
}
|
||||
|
||||
func (r *GetCommentResp) SetFromComment(comment *entity.Comment) {
|
||||
_ = copier.Copy(r, comment)
|
||||
r.CommentID = comment.ID
|
||||
r.CreatedAt = comment.CreatedAt.Unix()
|
||||
r.ReplyUserID = comment.GetReplyUserID()
|
||||
r.ReplyCommentID = comment.GetReplyCommentID()
|
||||
}
|
||||
|
||||
// GetCommentPersonalWithPageReq get comment list page request
|
||||
type GetCommentPersonalWithPageReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// username
|
||||
Username string `validate:"omitempty,gt=0,lte=100" form:"username"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// GetCommentPersonalWithPageResp comment response
|
||||
type GetCommentPersonalWithPageResp struct {
|
||||
// comment id
|
||||
CommentID string `json:"comment_id"`
|
||||
// create time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
// object id
|
||||
ObjectID string `json:"object_id"`
|
||||
// question id
|
||||
QuestionID string `json:"question_id"`
|
||||
// answer id
|
||||
AnswerID string `json:"answer_id"`
|
||||
// object type
|
||||
ObjectType string `json:"object_type" enums:"question,answer,tag,comment"`
|
||||
// title
|
||||
Title string `json:"title"`
|
||||
// url title
|
||||
UrlTitle string `json:"url_title"`
|
||||
// content
|
||||
Content string `json:"content"`
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// AddConfigReq add config request
|
||||
type AddConfigReq struct {
|
||||
// the config key
|
||||
Key string `validate:"omitempty,gt=0,lte=32" comment:"the config key" json:"key"`
|
||||
// the config value, custom data structures and types
|
||||
Value string `validate:"omitempty,gt=0,lte=128" comment:"the config value, custom data structures and types" json:"value"`
|
||||
}
|
||||
|
||||
// RemoveConfigReq delete config request
|
||||
type RemoveConfigReq struct {
|
||||
// config id
|
||||
ID int `validate:"required" comment:"config id" json:"id"`
|
||||
}
|
||||
|
||||
// UpdateConfigReq update config request
|
||||
type UpdateConfigReq struct {
|
||||
// config id
|
||||
ID int `validate:"required" comment:"config id" json:"id"`
|
||||
// the config key
|
||||
Key string `validate:"omitempty,gt=0,lte=32" comment:"the config key" json:"key"`
|
||||
// the config value, custom data structures and types
|
||||
Value string `validate:"omitempty,gt=0,lte=128" comment:"the config value, custom data structures and types" json:"value"`
|
||||
}
|
||||
|
||||
// GetConfigListReq get config list all request
|
||||
type GetConfigListReq struct {
|
||||
// the config key
|
||||
Key string `validate:"omitempty,gt=0,lte=32" comment:"the config key" form:"key"`
|
||||
// the config value, custom data structures and types
|
||||
Value string `validate:"omitempty,gt=0,lte=128" comment:"the config value, custom data structures and types" form:"value"`
|
||||
}
|
||||
|
||||
// GetConfigWithPageReq get config list page request
|
||||
type GetConfigWithPageReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// the config key
|
||||
Key string `validate:"omitempty,gt=0,lte=32" comment:"the config key" form:"key"`
|
||||
// the config value, custom data structures and types
|
||||
Value string `validate:"omitempty,gt=0,lte=128" comment:"the config value, custom data structures and types" form:"value"`
|
||||
}
|
||||
|
||||
// GetConfigResp get config response
|
||||
type GetConfigResp struct {
|
||||
// config id
|
||||
ID int `json:"id"`
|
||||
// the config key
|
||||
Key string `json:"key"`
|
||||
// the config value, custom data structures and types
|
||||
Value string `json:"value"`
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
type ConnectorInfoResp struct {
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
||||
type ConnectorUserInfoResp struct {
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Link string `json:"link"`
|
||||
Binding bool `json:"binding"`
|
||||
ExternalID string `json:"external_id"`
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
var AppStartTime time.Time
|
||||
|
||||
const (
|
||||
DashboardCacheKey = "answer:dashboard"
|
||||
DashboardCacheTime = 60 * time.Minute
|
||||
)
|
||||
|
||||
type DashboardInfo struct {
|
||||
QuestionCount int64 `json:"question_count"`
|
||||
ResolvedCount int64 `json:"resolved_count"`
|
||||
ResolvedRate string `json:"resolved_rate"`
|
||||
UnansweredCount int64 `json:"unanswered_count"`
|
||||
UnansweredRate string `json:"unanswered_rate"`
|
||||
AnswerCount int64 `json:"answer_count"`
|
||||
CommentCount int64 `json:"comment_count"`
|
||||
VoteCount int64 `json:"vote_count"`
|
||||
UserCount int64 `json:"user_count"`
|
||||
ReportCount int64 `json:"report_count"`
|
||||
UploadingFiles bool `json:"uploading_files"`
|
||||
SMTP string `json:"smtp"`
|
||||
HTTPS bool `json:"https"`
|
||||
TimeZone string `json:"time_zone"`
|
||||
OccupyingStorageSpace string `json:"occupying_storage_space"`
|
||||
AppStartTime string `json:"app_start_time"`
|
||||
VersionInfo DashboardInfoVersion `json:"version_info"`
|
||||
LoginRequired bool `json:"login_required"`
|
||||
GoVersion string `json:"go_version"`
|
||||
DatabaseVersion string `json:"database_version"`
|
||||
DatabaseSize string `json:"database_size"`
|
||||
}
|
||||
|
||||
type DashboardInfoVersion struct {
|
||||
Version string `json:"version"`
|
||||
Revision string `json:"revision"`
|
||||
RemoteVersion string `json:"remote_version"`
|
||||
}
|
||||
|
||||
type RemoteVersion struct {
|
||||
Release struct {
|
||||
Version string `json:"version"`
|
||||
URL string `json:"url"`
|
||||
} `json:"release"`
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
)
|
||||
|
||||
const (
|
||||
AccountActivationSourceType EmailSourceType = "account-activation"
|
||||
PasswordResetSourceType EmailSourceType = "password-reset"
|
||||
ConfirmNewEmailSourceType EmailSourceType = "password-reset"
|
||||
UnsubscribeSourceType EmailSourceType = "unsubscribe"
|
||||
BindingSourceType EmailSourceType = "binding"
|
||||
)
|
||||
|
||||
type EmailSourceType string
|
||||
|
||||
type EmailCodeContent struct {
|
||||
SourceType EmailSourceType `json:"source_type"`
|
||||
Email string `json:"e_mail"`
|
||||
UserID string `json:"user_id"`
|
||||
// Used for unsubscribe notification
|
||||
NotificationSources []constant.NotificationSource `json:"notification_source,omitempty"`
|
||||
// Used for third-party login account binding
|
||||
BindingKey string `json:"binding_key,omitempty"`
|
||||
// Skip the validation of the latest code
|
||||
SkipValidationLatestCode bool `json:"skip_validation_latest_code"`
|
||||
}
|
||||
|
||||
func (r *EmailCodeContent) ToJSONString() string {
|
||||
codeBytes, _ := json.Marshal(r)
|
||||
return string(codeBytes)
|
||||
}
|
||||
|
||||
func (r *EmailCodeContent) FromJSONString(data string) error {
|
||||
return json.Unmarshal([]byte(data), &r)
|
||||
}
|
||||
|
||||
type RegisterTemplateData struct {
|
||||
SiteName string
|
||||
RegisterUrl string
|
||||
}
|
||||
|
||||
type PassResetTemplateData struct {
|
||||
SiteName string
|
||||
PassResetUrl string
|
||||
}
|
||||
|
||||
type ChangeEmailTemplateData struct {
|
||||
SiteName string
|
||||
ChangeEmailUrl string
|
||||
}
|
||||
|
||||
type TestTemplateData struct {
|
||||
SiteName string
|
||||
}
|
||||
|
||||
type NewAnswerTemplateRawData struct {
|
||||
AnswerUserDisplayName string
|
||||
QuestionTitle string
|
||||
QuestionID string
|
||||
AnswerID string
|
||||
AnswerSummary string
|
||||
UnsubscribeCode string
|
||||
}
|
||||
|
||||
type NewAnswerTemplateData struct {
|
||||
SiteName string
|
||||
DisplayName string
|
||||
QuestionTitle string
|
||||
AnswerUrl string
|
||||
AnswerSummary string
|
||||
UnsubscribeUrl string
|
||||
}
|
||||
|
||||
type NewInviteAnswerTemplateRawData struct {
|
||||
InviterDisplayName string
|
||||
QuestionTitle string
|
||||
QuestionID string
|
||||
UnsubscribeCode string
|
||||
}
|
||||
|
||||
type NewInviteAnswerTemplateData struct {
|
||||
SiteName string
|
||||
DisplayName string
|
||||
QuestionTitle string
|
||||
InviteUrl string
|
||||
UnsubscribeUrl string
|
||||
}
|
||||
|
||||
type NewCommentTemplateRawData struct {
|
||||
CommentUserDisplayName string
|
||||
QuestionTitle string
|
||||
QuestionID string
|
||||
AnswerID string
|
||||
CommentID string
|
||||
CommentSummary string
|
||||
UnsubscribeCode string
|
||||
}
|
||||
|
||||
type NewCommentTemplateData struct {
|
||||
SiteName string
|
||||
DisplayName string
|
||||
QuestionTitle string
|
||||
CommentUrl string
|
||||
CommentSummary string
|
||||
UnsubscribeUrl string
|
||||
}
|
||||
|
||||
type NewQuestionTemplateRawData struct {
|
||||
QuestionAuthorUserID string
|
||||
QuestionTitle string
|
||||
QuestionID string
|
||||
UnsubscribeCode string
|
||||
Tags []string
|
||||
TagIDs []string
|
||||
}
|
||||
|
||||
type NewQuestionTemplateData struct {
|
||||
SiteName string
|
||||
QuestionTitle string
|
||||
QuestionUrl string
|
||||
Tags string
|
||||
UnsubscribeUrl string
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
type ErrTypeData struct {
|
||||
ErrType string `json:"err_type"`
|
||||
}
|
||||
|
||||
var ErrTypeModal = ErrTypeData{ErrType: "modal"}
|
||||
|
||||
var ErrTypeToast = ErrTypeData{ErrType: "toast"}
|
||||
|
||||
var ErrTypeAlert = ErrTypeData{ErrType: "alert"}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/pkg/uid"
|
||||
)
|
||||
|
||||
// EventMsg event message
|
||||
type EventMsg struct {
|
||||
EventType constant.EventType
|
||||
UserID string
|
||||
|
||||
TriggerObjectID string
|
||||
|
||||
QuestionID string
|
||||
QuestionUserID string
|
||||
|
||||
AnswerID string
|
||||
AnswerUserID string
|
||||
|
||||
CommentID string
|
||||
CommentUserID string
|
||||
|
||||
ExtraInfo map[string]string
|
||||
}
|
||||
|
||||
// NewEvent create a new event
|
||||
func NewEvent(e constant.EventType, userID string) *EventMsg {
|
||||
return &EventMsg{
|
||||
UserID: userID,
|
||||
EventType: e,
|
||||
ExtraInfo: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// QID get question id
|
||||
func (e *EventMsg) QID(questionID, userID string) *EventMsg {
|
||||
if len(questionID) > 0 {
|
||||
e.QuestionID = uid.DeShortID(questionID)
|
||||
}
|
||||
e.QuestionUserID = userID
|
||||
return e
|
||||
}
|
||||
|
||||
// AID get answer id
|
||||
func (e *EventMsg) AID(answerID, userID string) *EventMsg {
|
||||
if len(answerID) > 0 {
|
||||
e.AnswerID = uid.DeShortID(answerID)
|
||||
}
|
||||
e.AnswerUserID = userID
|
||||
return e
|
||||
}
|
||||
|
||||
// CID get comment id
|
||||
func (e *EventMsg) CID(comment, userID string) *EventMsg {
|
||||
e.CommentID = comment
|
||||
e.CommentUserID = userID
|
||||
return e
|
||||
}
|
||||
|
||||
// TID get trigger object id
|
||||
func (e *EventMsg) TID(triggerObjectID string) *EventMsg {
|
||||
if len(triggerObjectID) > 0 {
|
||||
e.TriggerObjectID = uid.DeShortID(triggerObjectID)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// AddExtra add extra info
|
||||
func (e *EventMsg) AddExtra(key, value string) *EventMsg {
|
||||
e.ExtraInfo[key] = value
|
||||
return e
|
||||
}
|
||||
|
||||
// GetExtra get extra info
|
||||
func (e *EventMsg) GetExtra(key string) string {
|
||||
if v, ok := e.ExtraInfo[key]; ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetObjectID get object id
|
||||
func (e *EventMsg) GetObjectID() string {
|
||||
if len(e.TriggerObjectID) > 0 {
|
||||
return e.TriggerObjectID
|
||||
}
|
||||
if len(e.CommentID) > 0 {
|
||||
return e.CommentID
|
||||
}
|
||||
if len(e.AnswerID) > 0 {
|
||||
return e.AnswerID
|
||||
}
|
||||
return e.QuestionID
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// FollowReq follow object request
|
||||
type FollowReq struct {
|
||||
// object id
|
||||
ObjectID string `validate:"required" form:"object_id" json:"object_id"`
|
||||
// is cancel
|
||||
IsCancel bool `validate:"omitempty" form:"is_cancel" json:"is_cancel"`
|
||||
}
|
||||
|
||||
// FollowResp response object's follows and current user follow status
|
||||
type FollowResp struct {
|
||||
// the followers of object
|
||||
Follows int `json:"follows"`
|
||||
// if user is followed object will be true,otherwise false
|
||||
IsFollowed bool `json:"is_followed"`
|
||||
}
|
||||
|
||||
type FollowDTO struct {
|
||||
// object TagID
|
||||
ObjectID string
|
||||
// is cancel
|
||||
IsCancel bool
|
||||
// user TagID
|
||||
UserID string
|
||||
}
|
||||
|
||||
// UpdateFollowTagsReq update user follow tags
|
||||
type UpdateFollowTagsReq struct {
|
||||
// tag slug name list
|
||||
SlugNameList []string `json:"slug_name_list"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
const (
|
||||
ForbiddenReasonTypeInactive = "inactive"
|
||||
ForbiddenReasonTypeURLExpired = "url_expired"
|
||||
ForbiddenReasonTypeUserSuspended = "suspended"
|
||||
)
|
||||
|
||||
// ForbiddenResp forbidden response
|
||||
type ForbiddenResp struct {
|
||||
// forbidden reason type
|
||||
Type string `json:"type" enums:"inactive,url_expired"`
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/apache/answer/pkg/converter"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const (
|
||||
MCPSearchCondKeyword = "keyword"
|
||||
MCPSearchCondUsername = "username"
|
||||
MCPSearchCondScore = "score"
|
||||
MCPSearchCondTag = "tag"
|
||||
MCPSearchCondPage = "page"
|
||||
MCPSearchCondPageSize = "page_size"
|
||||
MCPSearchCondTagName = "tag_name"
|
||||
MCPSearchCondQuestionID = "question_id"
|
||||
MCPSearchCondObjectID = "object_id"
|
||||
MCPSearchCondSemanticQuery = "query"
|
||||
MCPSearchCondTopK = "top_k"
|
||||
)
|
||||
|
||||
type MCPSearchCond struct {
|
||||
Keyword string `json:"keyword"`
|
||||
Username string `json:"username"`
|
||||
Score int `json:"score"`
|
||||
Tags []string `json:"tags"`
|
||||
QuestionID string `json:"question_id"`
|
||||
}
|
||||
|
||||
type MCPSearchQuestionDetail struct {
|
||||
QuestionID string `json:"question_id"`
|
||||
}
|
||||
|
||||
type MCPSearchCommentCond struct {
|
||||
ObjectID string `json:"object_id"`
|
||||
}
|
||||
|
||||
type MCPSearchTagCond struct {
|
||||
TagName string `json:"tag_name"`
|
||||
}
|
||||
|
||||
type MCPSearchUserCond struct {
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
type MCPSearchQuestionInfoResp struct {
|
||||
QuestionID string `json:"question_id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
||||
type MCPSearchAnswerInfoResp struct {
|
||||
QuestionID string `json:"question_id"`
|
||||
QuestionTitle string `json:"question_title,omitempty"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
AnswerContent string `json:"answer_content"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
||||
type MCPSearchTagResp struct {
|
||||
TagName string `json:"tag_name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Description string `json:"description"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
||||
type MCPSearchUserInfoResp struct {
|
||||
Username string `json:"username"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
||||
type MCPSearchCommentInfoResp struct {
|
||||
CommentID string `json:"comment_id"`
|
||||
Content string `json:"content"`
|
||||
ObjectID string `json:"object_id"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
||||
// MCPSemanticSearchCond is the condition for semantic search.
|
||||
type MCPSemanticSearchCond struct {
|
||||
Query string `json:"query"`
|
||||
TopK int `json:"top_k"`
|
||||
}
|
||||
|
||||
// MCPSemanticSearchResp is a single semantic search result.
|
||||
type MCPSemanticSearchResp struct {
|
||||
ObjectID string `json:"object_id"`
|
||||
ObjectType string `json:"object_type"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Score float64 `json:"score"`
|
||||
Link string `json:"link"`
|
||||
Answers []*MCPSemanticSearchAnswer `json:"answers,omitempty"`
|
||||
Comments []*MCPSemanticSearchComment `json:"comments,omitempty"`
|
||||
}
|
||||
|
||||
// MCPSemanticSearchAnswer is an answer in a semantic search result.
|
||||
type MCPSemanticSearchAnswer struct {
|
||||
AnswerID string `json:"answer_id"`
|
||||
Content string `json:"content"`
|
||||
Comments []*MCPSemanticSearchComment `json:"comments,omitempty"`
|
||||
}
|
||||
|
||||
// MCPSemanticSearchComment is a comment in a semantic search result.
|
||||
type MCPSemanticSearchComment struct {
|
||||
CommentID string `json:"comment_id"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func NewMCPSemanticSearchCond(request mcp.CallToolRequest) *MCPSemanticSearchCond {
|
||||
cond := &MCPSemanticSearchCond{TopK: 5}
|
||||
if query, ok := getRequestValue(request, MCPSearchCondSemanticQuery); ok {
|
||||
cond.Query = query
|
||||
}
|
||||
if topK, ok := getRequestNumber(request, MCPSearchCondTopK); ok && topK > 0 {
|
||||
cond.TopK = topK
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func NewMCPSearchCond(request mcp.CallToolRequest) *MCPSearchCond {
|
||||
cond := &MCPSearchCond{}
|
||||
if keyword, ok := getRequestValue(request, MCPSearchCondKeyword); ok {
|
||||
cond.Keyword = keyword
|
||||
}
|
||||
if username, ok := getRequestValue(request, MCPSearchCondUsername); ok {
|
||||
cond.Username = username
|
||||
}
|
||||
if score, ok := getRequestNumber(request, MCPSearchCondScore); ok {
|
||||
cond.Score = score
|
||||
}
|
||||
if tag, ok := getRequestValue(request, MCPSearchCondTag); ok {
|
||||
cond.Tags = strings.Split(tag, ",")
|
||||
}
|
||||
if questionID, ok := getRequestValue(request, MCPSearchCondQuestionID); ok {
|
||||
cond.QuestionID = questionID
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func NewMCPSearchAnswerCond(request mcp.CallToolRequest) *MCPSearchCond {
|
||||
cond := &MCPSearchCond{}
|
||||
if questionID, ok := getRequestValue(request, MCPSearchCondQuestionID); ok {
|
||||
cond.QuestionID = questionID
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func NewMCPSearchQuestionDetail(request mcp.CallToolRequest) *MCPSearchQuestionDetail {
|
||||
cond := &MCPSearchQuestionDetail{}
|
||||
if questionID, ok := getRequestValue(request, MCPSearchCondQuestionID); ok {
|
||||
cond.QuestionID = questionID
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func NewMCPSearchCommentCond(request mcp.CallToolRequest) *MCPSearchCommentCond {
|
||||
cond := &MCPSearchCommentCond{}
|
||||
if keyword, ok := getRequestValue(request, MCPSearchCondObjectID); ok {
|
||||
cond.ObjectID = keyword
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func NewMCPSearchTagCond(request mcp.CallToolRequest) *MCPSearchTagCond {
|
||||
cond := &MCPSearchTagCond{}
|
||||
if tagName, ok := getRequestValue(request, MCPSearchCondTagName); ok {
|
||||
cond.TagName = tagName
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func NewMCPSearchUserCond(request mcp.CallToolRequest) *MCPSearchUserCond {
|
||||
cond := &MCPSearchUserCond{}
|
||||
if username, ok := getRequestValue(request, MCPSearchCondUsername); ok {
|
||||
cond.Username = username
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func getRequestValue(request mcp.CallToolRequest, key string) (string, bool) {
|
||||
value, ok := request.GetArguments()[key].(string)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
return value, true
|
||||
}
|
||||
|
||||
func getRequestNumber(request mcp.CallToolRequest, key string) (int, bool) {
|
||||
value, ok := request.GetArguments()[key].(float64)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
return int(value), true
|
||||
}
|
||||
|
||||
func (cond *MCPSearchCond) ToQueryString() string {
|
||||
var queryBuilder strings.Builder
|
||||
if len(cond.Keyword) > 0 {
|
||||
queryBuilder.WriteString(cond.Keyword)
|
||||
}
|
||||
if len(cond.Username) > 0 {
|
||||
queryBuilder.WriteString(" user:" + cond.Username)
|
||||
}
|
||||
if cond.Score > 0 {
|
||||
queryBuilder.WriteString(" score:" + converter.IntToString(int64(cond.Score)))
|
||||
}
|
||||
if len(cond.Tags) > 0 {
|
||||
for _, tag := range cond.Tags {
|
||||
queryBuilder.WriteString(" [" + tag + "]")
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(queryBuilder.String())
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package mcp_tools
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/schema"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
var (
|
||||
MCPToolsList = []mcp.Tool{
|
||||
NewQuestionsTool(),
|
||||
NewAnswersTool(),
|
||||
NewCommentsTool(),
|
||||
NewTagsTool(),
|
||||
NewTagDetailTool(),
|
||||
NewUserTool(),
|
||||
NewSemanticSearchTool(),
|
||||
}
|
||||
)
|
||||
|
||||
func NewQuestionsTool() mcp.Tool {
|
||||
listFilesTool := mcp.NewTool("get_questions",
|
||||
mcp.WithDescription("Searching for questions that already existed in the system. After the search, you can use the get_answers_by_question_id tool to get answers for the questions."),
|
||||
mcp.WithString(schema.MCPSearchCondKeyword,
|
||||
mcp.Description("Keyword to search for questions. Multiple keywords separated by spaces"),
|
||||
),
|
||||
mcp.WithString(schema.MCPSearchCondUsername,
|
||||
mcp.Description("Search for questions that contain only those created by the specified user"),
|
||||
),
|
||||
mcp.WithString(schema.MCPSearchCondTag,
|
||||
mcp.Description("Filter by tag (semicolon separated for multiple tags)"),
|
||||
),
|
||||
mcp.WithString(schema.MCPSearchCondScore,
|
||||
mcp.Description("Minimum score that the question must have"),
|
||||
),
|
||||
)
|
||||
return listFilesTool
|
||||
}
|
||||
|
||||
func NewAnswersTool() mcp.Tool {
|
||||
listFilesTool := mcp.NewTool("get_answers_by_question_id",
|
||||
mcp.WithDescription("Search for all answers corresponding to the question ID. The question ID is provided by get_questions tool."),
|
||||
mcp.WithString(schema.MCPSearchCondQuestionID,
|
||||
mcp.Description("The ID of the question to which the answer belongs. The question ID is provided by get_questions tool."),
|
||||
),
|
||||
)
|
||||
return listFilesTool
|
||||
}
|
||||
|
||||
func NewCommentsTool() mcp.Tool {
|
||||
listFilesTool := mcp.NewTool("get_comments",
|
||||
mcp.WithDescription("Searching for comments that already existed in the system"),
|
||||
mcp.WithString(schema.MCPSearchCondObjectID,
|
||||
mcp.Description("Queries comments on an object, either a question or an answer. object_id is the id of the object."),
|
||||
),
|
||||
)
|
||||
return listFilesTool
|
||||
}
|
||||
|
||||
func NewTagsTool() mcp.Tool {
|
||||
listFilesTool := mcp.NewTool("get_tags",
|
||||
mcp.WithDescription("Searching for tags that already existed in the system"),
|
||||
mcp.WithString(schema.MCPSearchCondTagName,
|
||||
mcp.Description("Tag name"),
|
||||
),
|
||||
)
|
||||
return listFilesTool
|
||||
}
|
||||
|
||||
func NewTagDetailTool() mcp.Tool {
|
||||
listFilesTool := mcp.NewTool("get_tag_detail",
|
||||
mcp.WithDescription("Get detailed information about a specific tag"),
|
||||
mcp.WithString(schema.MCPSearchCondTagName,
|
||||
mcp.Description("Tag name"),
|
||||
),
|
||||
)
|
||||
return listFilesTool
|
||||
}
|
||||
|
||||
func NewUserTool() mcp.Tool {
|
||||
listFilesTool := mcp.NewTool("get_user",
|
||||
mcp.WithDescription("Searching for users that already existed in the system"),
|
||||
mcp.WithString(schema.MCPSearchCondUsername,
|
||||
mcp.Description("Username"),
|
||||
),
|
||||
)
|
||||
return listFilesTool
|
||||
}
|
||||
|
||||
func NewSemanticSearchTool() mcp.Tool {
|
||||
tool := mcp.NewTool("semantic_search",
|
||||
mcp.WithDescription("Search questions and answers by semantic meaning. Use this when the user's question relates conceptually to existing content but may not match exact keywords. Returns the most semantically similar content."),
|
||||
mcp.WithString(schema.MCPSearchCondSemanticQuery,
|
||||
mcp.Required(),
|
||||
mcp.Description("The search query text to find semantically similar questions and answers"),
|
||||
),
|
||||
mcp.WithNumber(schema.MCPSearchCondTopK,
|
||||
mcp.Description("Maximum number of results to return (default 5)"),
|
||||
),
|
||||
)
|
||||
return tool
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import "slices"
|
||||
|
||||
type UpdateReactionReq struct {
|
||||
ObjectID string `validate:"required" json:"object_id"`
|
||||
Emoji string `validate:"required,oneof=heart smile frown" json:"emoji"`
|
||||
Reaction string `validate:"required,oneof=activate deactivate" json:"reaction"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type GetReactionReq struct {
|
||||
ObjectID string `validate:"required" form:"object_id"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// ReactionsSummaryMeta reactions summary meta
|
||||
type ReactionsSummaryMeta struct {
|
||||
Reactions []*ReactionSummaryMeta `json:"reactions"`
|
||||
}
|
||||
|
||||
// ReactionSummaryMeta reaction summary meta
|
||||
type ReactionSummaryMeta struct {
|
||||
Emoji string `json:"emoji"`
|
||||
UserIDs []string `json:"user_ids"`
|
||||
}
|
||||
|
||||
// AddReactionSummary add user operation to reaction summary
|
||||
func (r *ReactionsSummaryMeta) AddReactionSummary(emoji, userID string) {
|
||||
for _, reaction := range r.Reactions {
|
||||
if reaction.Emoji != emoji {
|
||||
continue
|
||||
}
|
||||
exist := slices.Contains(reaction.UserIDs, userID)
|
||||
if !exist {
|
||||
reaction.UserIDs = append(reaction.UserIDs, userID)
|
||||
}
|
||||
return
|
||||
}
|
||||
r.Reactions = append(r.Reactions, &ReactionSummaryMeta{
|
||||
Emoji: emoji,
|
||||
UserIDs: []string{userID},
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveReactionSummary remove user operation from reaction summary
|
||||
func (r *ReactionsSummaryMeta) RemoveReactionSummary(emoji, userID string) {
|
||||
updatedReactions := make([]*ReactionSummaryMeta, 0)
|
||||
for _, reaction := range r.Reactions {
|
||||
if reaction.Emoji != emoji && len(reaction.UserIDs) > 0 {
|
||||
updatedReactions = append(updatedReactions, reaction)
|
||||
continue
|
||||
}
|
||||
updatedUserIDs := make([]string, 0, len(r.Reactions))
|
||||
for _, id := range reaction.UserIDs {
|
||||
if id != userID {
|
||||
updatedUserIDs = append(updatedUserIDs, id)
|
||||
}
|
||||
}
|
||||
if len(updatedUserIDs) > 0 {
|
||||
reaction.UserIDs = updatedUserIDs
|
||||
updatedReactions = append(updatedReactions, reaction)
|
||||
}
|
||||
}
|
||||
r.Reactions = updatedReactions
|
||||
}
|
||||
|
||||
// CheckUserInReactionSummary check user's operation if in reaction summary
|
||||
func (r *ReactionsSummaryMeta) CheckUserInReactionSummary(emoji, userID string) bool {
|
||||
for _, reaction := range r.Reactions {
|
||||
if reaction.Emoji != emoji {
|
||||
continue
|
||||
}
|
||||
if slices.Contains(reaction.UserIDs, userID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetReactionByObjectIdResp get reaction by object id response
|
||||
type GetReactionByObjectIdResp struct {
|
||||
ReactionSummary []*ReactionRespItem `json:"reaction_summary"`
|
||||
}
|
||||
|
||||
// ReactionRespItem reaction response item
|
||||
type ReactionRespItem struct {
|
||||
// Emoji is the reaction emoji
|
||||
Emoji string `json:"emoji"`
|
||||
// Count is the number of users who reacted
|
||||
Count int `json:"count"`
|
||||
// Tooltip is the user's name who reacted
|
||||
Tooltip string `json:"tooltip"`
|
||||
// IsActive is if current user has reacted
|
||||
IsActive bool `json:"is_active"`
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/entity"
|
||||
"github.com/apache/answer/pkg/uid"
|
||||
)
|
||||
|
||||
type ExternalNotificationMsg struct {
|
||||
ReceiverUserID string `json:"receiver_user_id"`
|
||||
ReceiverEmail string `json:"receiver_email"`
|
||||
ReceiverLang string `json:"receiver_lang"`
|
||||
|
||||
NewAnswerTemplateRawData *NewAnswerTemplateRawData `json:"new_answer_template_raw_data,omitempty"`
|
||||
NewInviteAnswerTemplateRawData *NewInviteAnswerTemplateRawData `json:"new_invite_answer_template_raw_data,omitempty"`
|
||||
NewCommentTemplateRawData *NewCommentTemplateRawData `json:"new_comment_template_raw_data,omitempty"`
|
||||
NewQuestionTemplateRawData *NewQuestionTemplateRawData `json:"new_question_template_raw_data,omitempty"`
|
||||
}
|
||||
|
||||
func CreateNewQuestionNotificationMsg(
|
||||
questionID, questionTitle, questionAuthorUserID string, tags []*entity.Tag) *ExternalNotificationMsg {
|
||||
questionID = uid.DeShortID(questionID)
|
||||
msg := &ExternalNotificationMsg{
|
||||
NewQuestionTemplateRawData: &NewQuestionTemplateRawData{
|
||||
QuestionAuthorUserID: questionAuthorUserID,
|
||||
QuestionID: questionID,
|
||||
QuestionTitle: questionTitle,
|
||||
},
|
||||
}
|
||||
for _, tag := range tags {
|
||||
msg.NewQuestionTemplateRawData.Tags = append(msg.NewQuestionTemplateRawData.Tags, tag.SlugName)
|
||||
msg.NewQuestionTemplateRawData.TagIDs = append(msg.NewQuestionTemplateRawData.TagIDs, tag.ID)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
"github.com/apache/answer/internal/entity"
|
||||
)
|
||||
|
||||
const (
|
||||
NotificationTypeInbox = 1
|
||||
NotificationTypeAchievement = 2
|
||||
NotificationNotRead = 1
|
||||
NotificationRead = 2
|
||||
NotificationStatusNormal = 1
|
||||
NotificationStatusDelete = 10
|
||||
NotificationInboxTypeAll = 0
|
||||
NotificationInboxTypePosts = 1
|
||||
NotificationInboxTypeVotes = 2
|
||||
NotificationInboxTypeInvites = 3
|
||||
)
|
||||
|
||||
var NotificationType = map[string]int{
|
||||
"inbox": NotificationTypeInbox,
|
||||
"achievement": NotificationTypeAchievement,
|
||||
}
|
||||
|
||||
var NotificationInboxType = map[string]int{
|
||||
"all": NotificationInboxTypeAll,
|
||||
"posts": NotificationInboxTypePosts,
|
||||
"invites": NotificationInboxTypeInvites,
|
||||
"votes": NotificationInboxTypeVotes,
|
||||
}
|
||||
|
||||
type NotificationContent struct {
|
||||
ID string `json:"id"`
|
||||
TriggerUserID string `json:"-"` // show userid
|
||||
ReceiverUserID string `json:"-"` // receiver userid
|
||||
UserInfo *UserBasicInfo `json:"user_info,omitempty"`
|
||||
ObjectInfo ObjectInfo `json:"object_info"`
|
||||
Rank int `json:"rank"`
|
||||
NotificationAction string `json:"notification_action,omitempty"`
|
||||
Type int `json:"-"` // 1 inbox 2 achievement
|
||||
IsRead bool `json:"is_read"`
|
||||
UpdateTime int64 `json:"update_time"`
|
||||
}
|
||||
|
||||
type GetRedDot struct {
|
||||
CanReviewQuestion bool `json:"-"`
|
||||
CanReviewAnswer bool `json:"-"`
|
||||
CanReviewTag bool `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
// NotificationMsg notification message
|
||||
type NotificationMsg struct {
|
||||
// trigger notification user id
|
||||
TriggerUserID string
|
||||
// receive notification user id
|
||||
ReceiverUserID string
|
||||
// type 1 inbox 2 achievement
|
||||
Type int
|
||||
// notification title
|
||||
Title string
|
||||
// notification object
|
||||
ObjectID string
|
||||
// notification object type
|
||||
ObjectType string
|
||||
// notification action
|
||||
NotificationAction string
|
||||
// if true no need to send notification to all followers
|
||||
NoNeedPushAllFollow bool
|
||||
// extra info
|
||||
ExtraInfo map[string]string
|
||||
}
|
||||
|
||||
type ObjectInfo struct {
|
||||
Title string `json:"title"`
|
||||
ObjectID string `json:"object_id"`
|
||||
ObjectMap map[string]string `json:"object_map"`
|
||||
ObjectType string `json:"object_type"`
|
||||
}
|
||||
|
||||
type RedDot struct {
|
||||
Inbox int64 `json:"inbox"`
|
||||
Achievement int64 `json:"achievement"`
|
||||
Revision int64 `json:"revision"`
|
||||
CanRevision bool `json:"can_revision"`
|
||||
BadgeAward *RedDotBadgeAward `json:"badge_award"`
|
||||
}
|
||||
|
||||
type RedDotBadgeAward struct {
|
||||
NotificationID string `json:"notification_id"`
|
||||
BadgeID string `json:"badge_id"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Level entity.BadgeLevel `json:"level"`
|
||||
}
|
||||
|
||||
type RedDotBadgeAwardCache struct {
|
||||
BadgeAwardList map[string]*RedDotBadgeAward `json:"badge_award_list"`
|
||||
}
|
||||
|
||||
// NewRedDotBadgeAwardCache new red dot badge award cache
|
||||
func NewRedDotBadgeAwardCache() *RedDotBadgeAwardCache {
|
||||
return &RedDotBadgeAwardCache{
|
||||
BadgeAwardList: make(map[string]*RedDotBadgeAward),
|
||||
}
|
||||
}
|
||||
|
||||
// GetBadgeAward get badge award
|
||||
func (r *RedDotBadgeAwardCache) GetBadgeAward() *RedDotBadgeAward {
|
||||
if len(r.BadgeAwardList) == 0 {
|
||||
return nil
|
||||
}
|
||||
var ids []string
|
||||
for _, v := range r.BadgeAwardList {
|
||||
ids = append(ids, v.NotificationID)
|
||||
}
|
||||
sort.Strings(ids)
|
||||
return r.BadgeAwardList[ids[0]]
|
||||
}
|
||||
|
||||
// FromJSON from json
|
||||
func (r *RedDotBadgeAwardCache) FromJSON(data string) {
|
||||
_ = json.Unmarshal([]byte(data), r)
|
||||
}
|
||||
|
||||
// ToJSON to json
|
||||
func (r *RedDotBadgeAwardCache) ToJSON() string {
|
||||
data, _ := json.Marshal(r)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// AddBadgeAward add badge award
|
||||
func (r *RedDotBadgeAwardCache) AddBadgeAward(badgeAward *RedDotBadgeAward) {
|
||||
if r.BadgeAwardList == nil {
|
||||
r.BadgeAwardList = make(map[string]*RedDotBadgeAward)
|
||||
}
|
||||
r.BadgeAwardList[badgeAward.NotificationID] = badgeAward
|
||||
}
|
||||
|
||||
// RemoveBadgeAward remove badge award
|
||||
func (r *RedDotBadgeAwardCache) RemoveBadgeAward(notificationID string) {
|
||||
if r.BadgeAwardList == nil {
|
||||
return
|
||||
}
|
||||
delete(r.BadgeAwardList, notificationID)
|
||||
}
|
||||
|
||||
type NotificationSearch struct {
|
||||
Page int `json:"page" form:"page"` // Query number of pages
|
||||
PageSize int `json:"page_size" form:"page_size"` // Search page size
|
||||
Type int `json:"-" form:"-"`
|
||||
TypeStr string `json:"type" form:"type"` // inbox achievement
|
||||
InboxTypeStr string `json:"inbox_type" form:"inbox_type"` // inbox achievement
|
||||
InboxType int `json:"-" form:"-"` // inbox achievement
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type NotificationClearRequest struct {
|
||||
NotificationType string `validate:"required,oneof=inbox achievement" json:"type"`
|
||||
UserID string `json:"-"`
|
||||
CanReviewQuestion bool `json:"-"`
|
||||
CanReviewAnswer bool `json:"-"`
|
||||
CanReviewTag bool `json:"-"`
|
||||
}
|
||||
|
||||
type NotificationClearIDRequest struct {
|
||||
UserID string `json:"-"`
|
||||
ID string `json:"id" form:"id"`
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/apache/answer/internal/base/translator"
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
)
|
||||
|
||||
// PermissionTrTplData template data as for translate permission message
|
||||
type PermissionTrTplData struct {
|
||||
Rank int
|
||||
}
|
||||
|
||||
// PermissionMemberAction permission member action
|
||||
type PermissionMemberAction struct {
|
||||
Action string `json:"action"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// GetPermissionReq get permission request
|
||||
type GetPermissionReq struct {
|
||||
Action string `form:"action"`
|
||||
Actions []string `validate:"omitempty" form:"actions"`
|
||||
}
|
||||
|
||||
func (r *GetPermissionReq) Check() (errField []*validator.FormErrorField, err error) {
|
||||
if len(r.Action) > 0 {
|
||||
r.Actions = strings.Split(r.Action, ",")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetPermissionResp get permission response
|
||||
type GetPermissionResp struct {
|
||||
HasPermission bool `json:"has_permission"`
|
||||
// only not allow, will return this tip
|
||||
NoPermissionTip string `json:"no_permission_tip"`
|
||||
}
|
||||
|
||||
func (r *GetPermissionResp) TrTip(lang i18n.Language, requireRank int) {
|
||||
if r.HasPermission {
|
||||
return
|
||||
}
|
||||
if requireRank <= 0 {
|
||||
r.NoPermissionTip = translator.Tr(lang, reason.RankFailToMeetTheCondition)
|
||||
} else {
|
||||
r.NoPermissionTip = translator.TrWithData(
|
||||
lang, reason.NoEnoughRankToOperate, &PermissionTrTplData{Rank: requireRank})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/plugin"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
PluginStatusActive PluginStatus = "active"
|
||||
PluginStatusInactive PluginStatus = "inactive"
|
||||
)
|
||||
|
||||
type PluginStatus string
|
||||
|
||||
type GetPluginListReq struct {
|
||||
Status PluginStatus `form:"status"`
|
||||
HaveConfig bool `form:"have_config"`
|
||||
}
|
||||
|
||||
type GetPluginListResp struct {
|
||||
Name string `json:"name"`
|
||||
SlugName string `json:"slug_name"`
|
||||
Description string `json:"description"`
|
||||
Version string `json:"version"`
|
||||
Enabled bool `json:"enabled"`
|
||||
HaveConfig bool `json:"have_config"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
||||
type GetAllPluginStatusResp struct {
|
||||
SlugName string `json:"slug_name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type UpdatePluginStatusReq struct {
|
||||
PluginSlugName string `validate:"required,gt=1,lte=100" json:"plugin_slug_name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type GetPluginConfigReq struct {
|
||||
PluginSlugName string `validate:"required,gt=1,lte=100" form:"plugin_slug_name"`
|
||||
}
|
||||
|
||||
type GetPluginConfigResp struct {
|
||||
Name string `json:"name"`
|
||||
SlugName string `json:"slug_name"`
|
||||
Description string `json:"description"`
|
||||
Version string `json:"version"`
|
||||
ConfigFields []ConfigField `json:"config_fields"`
|
||||
}
|
||||
|
||||
func (g *GetPluginConfigResp) SetConfigFields(ctx *gin.Context, fields []plugin.ConfigField) {
|
||||
for _, field := range fields {
|
||||
configField := ConfigField{
|
||||
Name: field.Name,
|
||||
Type: string(field.Type),
|
||||
Title: field.Title.Translate(ctx),
|
||||
Description: field.Description.Translate(ctx),
|
||||
Required: field.Required,
|
||||
Value: field.Value,
|
||||
UIOptions: ConfigFieldUIOptions{
|
||||
Rows: field.UIOptions.Rows,
|
||||
InputType: string(field.UIOptions.InputType),
|
||||
Variant: field.UIOptions.Variant,
|
||||
ClassName: field.UIOptions.ClassName,
|
||||
FieldClassName: field.UIOptions.FieldClassName,
|
||||
},
|
||||
}
|
||||
configField.UIOptions.Placeholder = field.UIOptions.Placeholder.Translate(ctx)
|
||||
configField.UIOptions.Label = field.UIOptions.Label.Translate(ctx)
|
||||
configField.UIOptions.Text = field.UIOptions.Text.Translate(ctx)
|
||||
if field.UIOptions.Action != nil {
|
||||
uiOptionAction := &UIOptionAction{
|
||||
Url: field.UIOptions.Action.Url,
|
||||
Method: field.UIOptions.Action.Method,
|
||||
}
|
||||
if field.UIOptions.Action.Loading != nil {
|
||||
uiOptionAction.Loading = &LoadingAction{
|
||||
Text: field.UIOptions.Action.Loading.Text.Translate(ctx),
|
||||
State: string(field.UIOptions.Action.Loading.State),
|
||||
}
|
||||
}
|
||||
if field.UIOptions.Action.OnComplete != nil {
|
||||
uiOptionAction.OnCompleteAction = &OnCompleteAction{
|
||||
ToastReturnMessage: field.UIOptions.Action.OnComplete.ToastReturnMessage,
|
||||
RefreshFormConfig: field.UIOptions.Action.OnComplete.RefreshFormConfig,
|
||||
}
|
||||
}
|
||||
configField.UIOptions.Action = uiOptionAction
|
||||
}
|
||||
|
||||
for _, option := range field.Options {
|
||||
configField.Options = append(configField.Options, ConfigFieldOption{
|
||||
Label: option.Label.Translate(ctx),
|
||||
Value: option.Value,
|
||||
})
|
||||
}
|
||||
g.ConfigFields = append(g.ConfigFields, configField)
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigField struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Required bool `json:"required"`
|
||||
Value any `json:"value"`
|
||||
UIOptions ConfigFieldUIOptions `json:"ui_options"`
|
||||
Options []ConfigFieldOption `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigFieldUIOptions struct {
|
||||
Placeholder string `json:"placeholder,omitempty"`
|
||||
Rows string `json:"rows,omitempty"`
|
||||
InputType string `json:"input_type,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Action *UIOptionAction `json:"action,omitempty"`
|
||||
Variant string `json:"variant,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
ClassName string `json:"class_name,omitempty"`
|
||||
FieldClassName string `json:"field_class_name,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigFieldOption struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type UIOptionAction struct {
|
||||
Url string `json:"url"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Loading *LoadingAction `json:"loading,omitempty"`
|
||||
OnCompleteAction *OnCompleteAction `json:"on_complete,omitempty"`
|
||||
}
|
||||
|
||||
type LoadingAction struct {
|
||||
Text string `json:"text"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
type OnCompleteAction struct {
|
||||
ToastReturnMessage bool `json:"toast_return_message"`
|
||||
RefreshFormConfig bool `json:"refresh_form_config"`
|
||||
}
|
||||
|
||||
type UpdatePluginConfigReq struct {
|
||||
PluginSlugName string `validate:"required,gt=1,lte=100" json:"plugin_slug_name"`
|
||||
ConfigFields map[string]any `json:"config_fields"`
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
type UserCenterAgentResp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
AgentInfo *AgentInfo `json:"agent_info"`
|
||||
}
|
||||
|
||||
type AgentInfo struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Icon string `json:"icon"`
|
||||
Url string `json:"url"`
|
||||
LoginRedirectURL string `json:"login_redirect_url"`
|
||||
SignUpRedirectURL string `json:"sign_up_redirect_url"`
|
||||
ControlCenterItems []*ControlCenter `json:"control_center"`
|
||||
EnabledOriginalUserSystem bool `json:"enabled_original_user_system"`
|
||||
}
|
||||
|
||||
type ControlCenter struct {
|
||||
Name string `json:"name"`
|
||||
Label string `json:"label"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type UserCenterPersonalBranding struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
PersonalBranding []*PersonalBranding `json:"personal_branding"`
|
||||
}
|
||||
|
||||
type PersonalBranding struct {
|
||||
Icon string `json:"icon"`
|
||||
Name string `json:"name"`
|
||||
Label string `json:"label"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/plugin"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type GetUserPluginListResp struct {
|
||||
Name string `json:"name"`
|
||||
SlugName string `json:"slug_name"`
|
||||
}
|
||||
|
||||
type UpdateUserPluginReq struct {
|
||||
PluginSlugName string `validate:"required,gt=1,lte=100" json:"plugin_slug_name"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type GetUserPluginConfigReq struct {
|
||||
PluginSlugName string `validate:"required,gt=1,lte=100" form:"plugin_slug_name"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type GetUserPluginConfigResp struct {
|
||||
Name string `json:"name"`
|
||||
SlugName string `json:"slug_name"`
|
||||
ConfigFields []*ConfigField `json:"config_fields"`
|
||||
}
|
||||
|
||||
func (g *GetUserPluginConfigResp) SetConfigFields(ctx *gin.Context, fields []plugin.ConfigField) {
|
||||
for _, field := range fields {
|
||||
configField := &ConfigField{
|
||||
Name: field.Name,
|
||||
Type: string(field.Type),
|
||||
Title: field.Title.Translate(ctx),
|
||||
Description: field.Description.Translate(ctx),
|
||||
Required: field.Required,
|
||||
Value: field.Value,
|
||||
UIOptions: ConfigFieldUIOptions{
|
||||
Rows: field.UIOptions.Rows,
|
||||
InputType: string(field.UIOptions.InputType),
|
||||
Variant: field.UIOptions.Variant,
|
||||
ClassName: field.UIOptions.ClassName,
|
||||
FieldClassName: field.UIOptions.FieldClassName,
|
||||
},
|
||||
}
|
||||
configField.UIOptions.Placeholder = field.UIOptions.Placeholder.Translate(ctx)
|
||||
configField.UIOptions.Label = field.UIOptions.Label.Translate(ctx)
|
||||
configField.UIOptions.Text = field.UIOptions.Text.Translate(ctx)
|
||||
if field.UIOptions.Action != nil {
|
||||
uiOptionAction := &UIOptionAction{
|
||||
Url: field.UIOptions.Action.Url,
|
||||
Method: field.UIOptions.Action.Method,
|
||||
}
|
||||
if field.UIOptions.Action.Loading != nil {
|
||||
uiOptionAction.Loading = &LoadingAction{
|
||||
Text: field.UIOptions.Action.Loading.Text.Translate(ctx),
|
||||
State: string(field.UIOptions.Action.Loading.State),
|
||||
}
|
||||
}
|
||||
if field.UIOptions.Action.OnComplete != nil {
|
||||
uiOptionAction.OnCompleteAction = &OnCompleteAction{
|
||||
ToastReturnMessage: field.UIOptions.Action.OnComplete.ToastReturnMessage,
|
||||
RefreshFormConfig: field.UIOptions.Action.OnComplete.RefreshFormConfig,
|
||||
}
|
||||
}
|
||||
configField.UIOptions.Action = uiOptionAction
|
||||
}
|
||||
|
||||
for _, option := range field.Options {
|
||||
configField.Options = append(configField.Options, ConfigFieldOption{
|
||||
Label: option.Label.Translate(ctx),
|
||||
Value: option.Value,
|
||||
})
|
||||
}
|
||||
g.ConfigFields = append(g.ConfigFields, configField)
|
||||
}
|
||||
}
|
||||
|
||||
type UpdateUserPluginConfigReq struct {
|
||||
PluginSlugName string `validate:"required,gt=1,lte=100" json:"plugin_slug_name"`
|
||||
ConfigFields map[string]any `json:"config_fields"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/apache/answer/internal/entity"
|
||||
"github.com/apache/answer/pkg/converter"
|
||||
"github.com/apache/answer/pkg/uid"
|
||||
)
|
||||
|
||||
const (
|
||||
QuestionOperationPin = "pin"
|
||||
QuestionOperationUnPin = "unpin"
|
||||
QuestionOperationHide = "hide"
|
||||
QuestionOperationShow = "show"
|
||||
)
|
||||
|
||||
// RemoveQuestionReq delete question request
|
||||
type RemoveQuestionReq struct {
|
||||
// question id
|
||||
ID string `validate:"required" json:"id"`
|
||||
UserID string `json:"-" ` // user_id
|
||||
IsAdmin bool `json:"-"`
|
||||
CaptchaID string `json:"captcha_id"` // captcha_id
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
type CloseQuestionReq struct {
|
||||
ID string `validate:"required" json:"id"`
|
||||
CloseType int `json:"close_type"` // close_type
|
||||
CloseMsg string `json:"close_msg"` // close_type
|
||||
UserID string `json:"-"` // user_id
|
||||
}
|
||||
|
||||
type OperationQuestionReq struct {
|
||||
ID string `validate:"required" json:"id"`
|
||||
Operation string `json:"operation"` // operation [pin unpin hide show]
|
||||
UserID string `json:"-"` // user_id
|
||||
CanPin bool `json:"-"`
|
||||
CanList bool `json:"-"`
|
||||
}
|
||||
|
||||
type CloseQuestionMeta struct {
|
||||
CloseType int `json:"close_type"`
|
||||
CloseMsg string `json:"close_msg"`
|
||||
}
|
||||
|
||||
// ReopenQuestionReq reopen question request
|
||||
type ReopenQuestionReq struct {
|
||||
QuestionID string `json:"question_id"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type QuestionAdd struct {
|
||||
// question title
|
||||
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
|
||||
// content
|
||||
Content string `validate:"gte=0,lte=65535" json:"content"`
|
||||
// html
|
||||
HTML string `json:"-"`
|
||||
// tags
|
||||
Tags []*TagItem `validate:"dive" json:"tags"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
QuestionPermission
|
||||
CaptchaID string `json:"captcha_id"` // captcha_id
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
IP string `json:"-"`
|
||||
UserAgent string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *QuestionAdd) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.HTML = converter.Markdown2HTML(req.Content)
|
||||
for _, tag := range req.Tags {
|
||||
if len(tag.OriginalText) > 0 {
|
||||
tag.ParsedText = converter.Markdown2HTML(tag.OriginalText)
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type QuestionAddByAnswer struct {
|
||||
// question title
|
||||
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
|
||||
// content
|
||||
Content string `validate:"gte=0,lte=65535" json:"content"`
|
||||
// html
|
||||
HTML string `json:"-"`
|
||||
AnswerContent string `validate:"required,notblank,gte=6,lte=65535" json:"answer_content"`
|
||||
AnswerHTML string `json:"-"`
|
||||
// tags
|
||||
Tags []*TagItem `validate:"dive" json:"tags"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
MentionUsernameList []string `validate:"omitempty" json:"mention_username_list"`
|
||||
QuestionPermission
|
||||
CaptchaID string `json:"captcha_id"` // captcha_id
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
IP string `json:"-"`
|
||||
UserAgent string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *QuestionAddByAnswer) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.HTML = converter.Markdown2HTML(req.Content)
|
||||
req.AnswerHTML = converter.Markdown2HTML(req.AnswerContent)
|
||||
for _, tag := range req.Tags {
|
||||
if len(tag.OriginalText) > 0 {
|
||||
tag.ParsedText = converter.Markdown2HTML(tag.OriginalText)
|
||||
}
|
||||
}
|
||||
if req.AnswerHTML == "" {
|
||||
errFields = append(errFields, &validator.FormErrorField{
|
||||
ErrorField: "answer_content",
|
||||
ErrorMsg: reason.AnswerContentCannotEmpty,
|
||||
})
|
||||
return errFields, errors.BadRequest(reason.QuestionContentCannotEmpty)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type QuestionPermission struct {
|
||||
IsAdminModerator bool `json:"-"`
|
||||
// whether user can add it
|
||||
CanAdd bool `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
// whether user can delete it
|
||||
CanDelete bool `json:"-"`
|
||||
// whether user can close it
|
||||
CanClose bool `json:"-"`
|
||||
// whether user can reopen it
|
||||
CanReopen bool `json:"-"`
|
||||
// whether user can pin it
|
||||
CanPin bool `json:"-"`
|
||||
CanUnPin bool `json:"-"`
|
||||
// whether user can hide it
|
||||
CanHide bool `json:"-"`
|
||||
CanShow bool `json:"-"`
|
||||
// whether user can use reserved it
|
||||
CanUseReservedTag bool `json:"-"`
|
||||
// whether user can invite other user to answer this question
|
||||
CanInviteOtherToAnswer bool `json:"-"`
|
||||
CanAddTag bool `json:"-"`
|
||||
CanRecover bool `json:"-"`
|
||||
}
|
||||
|
||||
type CheckCanQuestionUpdate struct {
|
||||
// question id
|
||||
ID string `validate:"required" form:"id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
type QuestionUpdate struct {
|
||||
// question id
|
||||
ID string `validate:"required" json:"id"`
|
||||
// question title
|
||||
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
|
||||
// content
|
||||
Content string `validate:"gte=0,lte=65535" json:"content"`
|
||||
// html
|
||||
HTML string `json:"-"`
|
||||
InviteUser []string `validate:"omitempty" json:"invite_user"`
|
||||
// tags
|
||||
Tags []*TagItem `validate:"dive" json:"tags"`
|
||||
// edit summary
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
NoNeedReview bool `json:"-"`
|
||||
QuestionPermission
|
||||
CaptchaID string `json:"captcha_id"` // captcha_id
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
type QuestionRecoverReq struct {
|
||||
QuestionID string `validate:"required" json:"question_id"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type QuestionUpdateInviteUser struct {
|
||||
ID string `validate:"required" json:"id"`
|
||||
InviteUser []string `validate:"omitempty" json:"invite_user"`
|
||||
UserID string `json:"-"`
|
||||
QuestionPermission
|
||||
CaptchaID string `json:"captcha_id"` // captcha_id
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
func (req *QuestionUpdate) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.HTML = converter.Markdown2HTML(req.Content)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type QuestionBaseInfo struct {
|
||||
ID string `json:"id" `
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
ViewCount int `json:"view_count"`
|
||||
AnswerCount int `json:"answer_count"`
|
||||
CollectionCount int `json:"collection_count"`
|
||||
FollowCount int `json:"follow_count"`
|
||||
Status string `json:"status"`
|
||||
AcceptedAnswer bool `json:"accepted_answer"`
|
||||
}
|
||||
|
||||
type QuestionInfoResp struct {
|
||||
ID string `json:"id" `
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
Content string `json:"content"`
|
||||
HTML string `json:"html"`
|
||||
Description string `json:"description"`
|
||||
Tags []*TagResp `json:"tags"`
|
||||
ViewCount int `json:"view_count"`
|
||||
UniqueViewCount int `json:"unique_view_count"`
|
||||
VoteCount int `json:"vote_count"`
|
||||
AnswerCount int `json:"answer_count"`
|
||||
CollectionCount int `json:"collection_count"`
|
||||
FollowCount int `json:"follow_count"`
|
||||
AcceptedAnswerID string `json:"accepted_answer_id"`
|
||||
LastAnswerID string `json:"last_answer_id"`
|
||||
CreateTime int64 `json:"create_time"`
|
||||
UpdateTime int64 `json:"-"`
|
||||
PostUpdateTime int64 `json:"update_time"`
|
||||
QuestionUpdateTime int64 `json:"edit_time"`
|
||||
Pin int `json:"pin"`
|
||||
Show int `json:"show"`
|
||||
Status int `json:"status"`
|
||||
Operation *Operation `json:"operation,omitempty"`
|
||||
UserID string `json:"-"`
|
||||
LastEditUserID string `json:"-"`
|
||||
LastAnsweredUserID string `json:"-"`
|
||||
UserInfo *UserBasicInfo `json:"user_info"`
|
||||
UpdateUserInfo *UserBasicInfo `json:"update_user_info,omitempty"`
|
||||
LastAnsweredUserInfo *UserBasicInfo `json:"last_answered_user_info,omitempty"`
|
||||
Answered bool `json:"answered"`
|
||||
FirstAnswerId string `json:"first_answer_id"`
|
||||
Collected bool `json:"collected"`
|
||||
VoteStatus string `json:"vote_status"`
|
||||
IsFollowed bool `json:"is_followed"`
|
||||
|
||||
// MemberActions
|
||||
MemberActions []*PermissionMemberAction `json:"member_actions"`
|
||||
ExtendsActions []*PermissionMemberAction `json:"extends_actions"`
|
||||
}
|
||||
|
||||
// UpdateQuestionResp update question resp
|
||||
type UpdateQuestionResp struct {
|
||||
UrlTitle string `json:"url_title"`
|
||||
WaitForReview bool `json:"wait_for_review"`
|
||||
}
|
||||
|
||||
type AdminQuestionInfo struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
VoteCount int `json:"vote_count"`
|
||||
Show int `json:"show"`
|
||||
Pin int `json:"pin"`
|
||||
AnswerCount int `json:"answer_count"`
|
||||
AcceptedAnswerID string `json:"accepted_answer_id"`
|
||||
CreateTime int64 `json:"create_time"`
|
||||
UpdateTime int64 `json:"update_time"`
|
||||
EditTime int64 `json:"edit_time"`
|
||||
UserID string `json:"-" `
|
||||
UserInfo *UserBasicInfo `json:"user_info"`
|
||||
}
|
||||
|
||||
type OperationLevel string
|
||||
|
||||
const (
|
||||
OperationLevelInfo OperationLevel = "info"
|
||||
OperationLevelDanger OperationLevel = "danger"
|
||||
OperationLevelWarning OperationLevel = "warning"
|
||||
OperationLevelSecondary OperationLevel = "secondary"
|
||||
)
|
||||
|
||||
type Operation struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Msg string `json:"msg"`
|
||||
Time int64 `json:"time"`
|
||||
Level OperationLevel `json:"level"`
|
||||
}
|
||||
|
||||
type GetCloseTypeResp struct {
|
||||
// report name
|
||||
Name string `json:"name"`
|
||||
// report description
|
||||
Description string `json:"description"`
|
||||
// report source
|
||||
Source string `json:"source"`
|
||||
// report type
|
||||
Type int `json:"type"`
|
||||
// is have content
|
||||
HaveContent bool `json:"have_content"`
|
||||
// content type
|
||||
ContentType string `json:"content_type"`
|
||||
}
|
||||
|
||||
type UserAnswerInfo struct {
|
||||
AnswerID string `json:"answer_id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
Accepted int `json:"accepted"`
|
||||
VoteCount int `json:"vote_count"`
|
||||
CreateTime int `json:"create_time"`
|
||||
UpdateTime int `json:"update_time"`
|
||||
QuestionInfo struct {
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
Tags []any `json:"tags"`
|
||||
} `json:"question_info"`
|
||||
}
|
||||
|
||||
type UserQuestionInfo struct {
|
||||
ID string `json:"question_id"`
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
VoteCount int `json:"vote_count"`
|
||||
Tags []any `json:"tags"`
|
||||
ViewCount int `json:"view_count"`
|
||||
AnswerCount int `json:"answer_count"`
|
||||
CollectionCount int `json:"collection_count"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
AcceptedAnswerID string `json:"accepted_answer_id"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
const (
|
||||
QuestionOrderCondNewest = "newest"
|
||||
QuestionOrderCondActive = "active"
|
||||
QuestionOrderCondHot = "hot"
|
||||
QuestionOrderCondScore = "score"
|
||||
QuestionOrderCondUnanswered = "unanswered"
|
||||
QuestionOrderCondRecommend = "recommend"
|
||||
QuestionOrderCondFrequent = "frequent"
|
||||
|
||||
// HotInDays limit max days of the hottest question
|
||||
HotInDays = 90
|
||||
)
|
||||
|
||||
// QuestionPageReq query questions page
|
||||
type QuestionPageReq struct {
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
OrderCond string `validate:"omitempty,oneof=newest active hot score unanswered recommend frequent" form:"order"`
|
||||
Tag string `validate:"omitempty,gt=0,lte=100" form:"tag"`
|
||||
Username string `validate:"omitempty,gt=0,lte=100" form:"username"`
|
||||
InDays int `validate:"omitempty,min=1" form:"in_days"`
|
||||
|
||||
LoginUserID string `json:"-"`
|
||||
UserIDBeSearched string `json:"-"`
|
||||
TagID string `json:"-"`
|
||||
ShowPending bool `json:"-"`
|
||||
}
|
||||
|
||||
const (
|
||||
QuestionPageRespOperationTypeAsked = "asked"
|
||||
QuestionPageRespOperationTypeAnswered = "answered"
|
||||
QuestionPageRespOperationTypeModified = "modified"
|
||||
)
|
||||
|
||||
type QuestionPageResp struct {
|
||||
ID string `json:"id" `
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
Description string `json:"description"`
|
||||
Pin int `json:"pin"` // 1: unpin, 2: pin
|
||||
Show int `json:"show"` // 0: show, 1: hide
|
||||
Status int `json:"status"`
|
||||
Tags []*TagResp `json:"tags"`
|
||||
|
||||
// question statistical information
|
||||
ViewCount int `json:"view_count"`
|
||||
UniqueViewCount int `json:"unique_view_count"`
|
||||
VoteCount int `json:"vote_count"`
|
||||
AnswerCount int `json:"answer_count"`
|
||||
CollectionCount int `json:"collection_count"`
|
||||
FollowCount int `json:"follow_count"`
|
||||
|
||||
// answer information
|
||||
AcceptedAnswerID string `json:"accepted_answer_id"`
|
||||
LastAnswerID string `json:"last_answer_id"`
|
||||
LastAnsweredUserID string `json:"-"`
|
||||
LastAnsweredAt time.Time `json:"-"`
|
||||
|
||||
// operator information
|
||||
OperatedAt int64 `json:"operated_at"`
|
||||
Operator *QuestionPageRespOperator `json:"operator"`
|
||||
OperationType string `json:"operation_type"`
|
||||
}
|
||||
|
||||
type QuestionPageRespOperator struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Rank int `json:"rank"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Status string `json:"status"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
type AdminQuestionPageReq struct {
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
StatusCond string `validate:"omitempty,oneof=normal closed deleted pending" form:"status"`
|
||||
Query string `validate:"omitempty,gt=0,lte=100" json:"query" form:"query" `
|
||||
Status int `json:"-"`
|
||||
LoginUserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *AdminQuestionPageReq) Check() (errField []*validator.FormErrorField, err error) {
|
||||
status, ok := entity.AdminQuestionSearchStatus[req.StatusCond]
|
||||
if ok {
|
||||
req.Status = status
|
||||
}
|
||||
if req.Status == 0 {
|
||||
req.Status = 1
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// AdminAnswerPageReq admin answer page req
|
||||
type AdminAnswerPageReq struct {
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
StatusCond string `validate:"omitempty,oneof=normal deleted pending" form:"status"`
|
||||
Query string `validate:"omitempty,gt=0,lte=100" form:"query"`
|
||||
QuestionID string `validate:"omitempty,gt=0,lte=24" form:"question_id"`
|
||||
QuestionTitle string `json:"-"`
|
||||
AnswerID string `json:"-"`
|
||||
Status int `json:"-"`
|
||||
LoginUserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *AdminAnswerPageReq) Check() (errField []*validator.FormErrorField, err error) {
|
||||
req.QuestionID = uid.DeShortID(req.QuestionID)
|
||||
if req.QuestionID == "0" {
|
||||
req.QuestionID = ""
|
||||
}
|
||||
|
||||
if status, ok := entity.AdminAnswerSearchStatus[req.StatusCond]; ok {
|
||||
req.Status = status
|
||||
}
|
||||
if req.Status == 0 {
|
||||
req.Status = 1
|
||||
}
|
||||
|
||||
// parse query condition
|
||||
if len(req.Query) > 0 {
|
||||
prefix := "answer:"
|
||||
if strings.Contains(req.Query, prefix) {
|
||||
req.AnswerID = uid.DeShortID(strings.TrimSpace(strings.TrimPrefix(req.Query, prefix)))
|
||||
} else {
|
||||
req.QuestionTitle = strings.TrimSpace(req.Query)
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type AdminUpdateQuestionStatusReq struct {
|
||||
QuestionID string `validate:"required" json:"question_id"`
|
||||
Status string `validate:"required,oneof=available closed deleted" json:"status"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type PersonalQuestionPageReq struct {
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
OrderCond string `validate:"omitempty,oneof=newest active hot score unanswered" form:"order"`
|
||||
Username string `validate:"omitempty,gt=0,lte=100" form:"username"`
|
||||
LoginUserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
type PersonalAnswerPageReq struct {
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
OrderCond string `validate:"omitempty,oneof=newest active hot score unanswered" form:"order"`
|
||||
Username string `validate:"omitempty,gt=0,lte=100" form:"username"`
|
||||
LoginUserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
type PersonalCollectionPageReq struct {
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type GetQuestionLinkReq struct {
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1,max=100" form:"page_size"`
|
||||
QuestionID string `validate:"required" form:"question_id"`
|
||||
OrderCond string `validate:"omitempty,oneof=newest active hot score unanswered recommend frequent" form:"order"`
|
||||
InDays int `validate:"omitempty,min=1" form:"in_days"`
|
||||
|
||||
LoginUserID string `json:"-"`
|
||||
}
|
||||
|
||||
type GetQuestionLinkResp struct {
|
||||
QuestionPageResp
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// GetRankPersonalWithPageReq get rank list page request
|
||||
type GetRankPersonalWithPageReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// username
|
||||
Username string `validate:"omitempty,gt=0,lte=100" form:"username"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// GetRankPersonalPageResp rank response
|
||||
type GetRankPersonalPageResp struct {
|
||||
// create time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
// object id
|
||||
ObjectID string `json:"object_id"`
|
||||
// question id
|
||||
QuestionID string `json:"question_id"`
|
||||
// answer id
|
||||
AnswerID string `json:"answer_id"`
|
||||
// object type
|
||||
ObjectType string `json:"object_type" enums:"question,answer,tag,comment"`
|
||||
// title
|
||||
Title string `json:"title"`
|
||||
// url title
|
||||
UrlTitle string `json:"url_title"`
|
||||
// content
|
||||
Content string `json:"content"`
|
||||
// reputation
|
||||
Reputation int `json:"reputation"`
|
||||
// rank type
|
||||
RankType string `json:"rank_type"`
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/base/translator"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
)
|
||||
|
||||
type ReasonItem struct {
|
||||
ReasonKey string `json:"reason_key"`
|
||||
ReasonType int `json:"reason_type"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
ContentType string `json:"content_type"`
|
||||
Placeholder string `json:"placeholder"`
|
||||
}
|
||||
|
||||
type ReasonReq struct {
|
||||
// ObjectType
|
||||
ObjectType string `validate:"required" form:"object_type" json:"object_type"`
|
||||
// Action
|
||||
Action string `validate:"required" form:"action" json:"action"`
|
||||
}
|
||||
|
||||
func (r *ReasonItem) Translate(keyPrefix string, lang i18n.Language) {
|
||||
trField := func(fieldName, fieldData string) string {
|
||||
// If fieldData is empty, means no need to translate
|
||||
if len(fieldData) == 0 {
|
||||
return fieldData
|
||||
}
|
||||
key := keyPrefix + "." + fieldName
|
||||
fieldTr := translator.Tr(lang, key)
|
||||
if fieldTr != key {
|
||||
// If i18n key exists, return i18n value
|
||||
return fieldTr
|
||||
}
|
||||
// If i18n key not exists, return fieldData original value
|
||||
return fieldData
|
||||
}
|
||||
|
||||
r.ReasonKey = keyPrefix
|
||||
r.Name = trField("name", r.Name)
|
||||
r.Description = trField("desc", r.Description)
|
||||
r.Placeholder = trField("placeholder", r.Placeholder)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// PostRenderReq post render request
|
||||
type PostRenderReq struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// AddReportReq add report request
|
||||
type AddReportReq struct {
|
||||
// object id
|
||||
ObjectID string `validate:"required,gt=0,lte=20" json:"object_id"`
|
||||
// report type
|
||||
ReportType int `validate:"required" json:"report_type"`
|
||||
// report content
|
||||
Content string `validate:"omitempty,gt=0,lte=500" json:"content"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
CaptchaID string `json:"captcha_id"` // captcha_id
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
// GetReportListReq get report list all request
|
||||
type GetReportListReq struct {
|
||||
// report source
|
||||
Source string `validate:"required,oneof=question answer comment" form:"source"`
|
||||
}
|
||||
|
||||
// GetReportTypeResp get report response
|
||||
type GetReportTypeResp struct {
|
||||
// report name
|
||||
Name string `json:"name"`
|
||||
// report description
|
||||
Description string `json:"description"`
|
||||
// report source
|
||||
Source string `json:"source"`
|
||||
// report type
|
||||
Type int `json:"type"`
|
||||
// is have content
|
||||
HaveContent bool `json:"have_content"`
|
||||
// content type
|
||||
ContentType string `json:"content_type"`
|
||||
}
|
||||
|
||||
// ReportHandleReq request handle request
|
||||
type ReportHandleReq struct {
|
||||
ID string `validate:"required" comment:"report id" form:"id" json:"id"`
|
||||
FlaggedType int `validate:"required" comment:"flagged type" form:"flagged_type" json:"flagged_type"`
|
||||
FlaggedContent string `validate:"omitempty" comment:"flagged content" form:"flagged_content" json:"flagged_content"`
|
||||
}
|
||||
|
||||
// GetReportListPageDTO report list data transfer object
|
||||
type GetReportListPageDTO struct {
|
||||
Page int
|
||||
PageSize int
|
||||
Status int
|
||||
}
|
||||
|
||||
// GetReportListPageResp get report list
|
||||
type GetReportListPageResp struct {
|
||||
FlagID string `json:"flag_id"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
ObjectID string `json:"object_id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
CommentID string `json:"comment_id"`
|
||||
ObjectType string `json:"object_type" enums:"question,answer,comment"`
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
OriginalText string `json:"original_text"`
|
||||
ParsedText string `json:"parsed_text"`
|
||||
AnswerCount int `json:"answer_count"`
|
||||
AnswerAccepted bool `json:"answer_accepted"`
|
||||
Tags []*TagResp `json:"tags"`
|
||||
ObjectStatus int `json:"object_status"`
|
||||
ObjectShowStatus int `json:"object_show_status"`
|
||||
AuthorUserInfo UserBasicInfo `json:"author_user_info"`
|
||||
SubmitAt int64 `json:"submit_at"`
|
||||
SubmitterUser UserBasicInfo `json:"submitter_user"`
|
||||
Reason *ReasonItem `json:"reason"`
|
||||
ReasonContent string `json:"reason_content"`
|
||||
}
|
||||
|
||||
// GetUnreviewedReportPostPageReq get unreviewed report post page request
|
||||
type GetUnreviewedReportPostPageReq struct {
|
||||
Page int `json:"page" form:"page"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
// ReviewReportReq review report request
|
||||
type ReviewReportReq struct {
|
||||
FlagID string `validate:"required" json:"flag_id"`
|
||||
OperationType string `validate:"required,oneof=edit_post close_post delete_post unlist_post ignore_report" json:"operation_type"`
|
||||
CloseType int `validate:"omitempty" json:"close_type"`
|
||||
CloseMsg string `validate:"omitempty" json:"close_msg"`
|
||||
Title string `validate:"omitempty,notblank,gte=6,lte=150" json:"title"`
|
||||
Content string `validate:"omitempty,notblank,gte=6,lte=65535" json:"content"`
|
||||
Tags []*TagItem `validate:"omitempty,dive" json:"tags"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/apache/answer/pkg/uid"
|
||||
)
|
||||
|
||||
// UpdateReviewReq update review request
|
||||
type UpdateReviewReq struct {
|
||||
ReviewID int `validate:"required" json:"review_id"`
|
||||
Status string `validate:"required,oneof=approve reject" json:"status"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
func (r *UpdateReviewReq) IsApprove() bool {
|
||||
return r.Status == "approve"
|
||||
}
|
||||
|
||||
func (r *UpdateReviewReq) IsReject() bool {
|
||||
return r.Status == "reject"
|
||||
}
|
||||
|
||||
// GetUnreviewedPostPageReq get review page request
|
||||
type GetUnreviewedPostPageReq struct {
|
||||
ObjectID string `validate:"omitempty" form:"object_id"`
|
||||
Page int `validate:"omitempty" form:"page"`
|
||||
ReviewerMapping map[string]string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
func (r *GetUnreviewedPostPageReq) Check() (errField []*validator.FormErrorField, err error) {
|
||||
if len(r.ObjectID) > 0 {
|
||||
r.Page = 1
|
||||
r.ObjectID = uid.DeShortID(r.ObjectID)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetUnreviewedPostPageResp get review page response
|
||||
type GetUnreviewedPostPageResp struct {
|
||||
ReviewID int `json:"review_id"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
ObjectID string `json:"object_id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
CommentID string `json:"comment_id"`
|
||||
ObjectType string `json:"object_type" enums:"question,answer,comment"`
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
OriginalText string `json:"original_text"`
|
||||
ParsedText string `json:"parsed_text"`
|
||||
Tags []*TagResp `json:"tags"`
|
||||
ObjectStatus int `json:"object_status"`
|
||||
ObjectShowStatus int `json:"object_show_status"`
|
||||
AuthorUserInfo UserBasicInfo `json:"author_user_info"`
|
||||
SubmitAt int64 `json:"submit_at"`
|
||||
SubmitterDisplayName string `json:"submitter_display_name"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
)
|
||||
|
||||
// AddRevisionDTO add revision request
|
||||
type AddRevisionDTO struct {
|
||||
// user id
|
||||
UserID string
|
||||
// object id
|
||||
ObjectID string
|
||||
// title
|
||||
Title string
|
||||
// content
|
||||
Content string
|
||||
// log
|
||||
Log string
|
||||
// status
|
||||
Status int
|
||||
}
|
||||
|
||||
// GetRevisionListReq get revision list all request
|
||||
type GetRevisionListReq struct {
|
||||
// object id
|
||||
ObjectID string `validate:"required" comment:"object_id" form:"object_id"`
|
||||
IsAdmin bool `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
const RevisionAuditApprove = "approve"
|
||||
const RevisionAuditReject = "reject"
|
||||
|
||||
type RevisionAuditReq struct {
|
||||
// object id
|
||||
ID string `validate:"required" comment:"id" form:"id"`
|
||||
Operation string `validate:"required" comment:"operation" form:"operation"` // approve or reject
|
||||
UserID string `json:"-"`
|
||||
CanReviewQuestion bool `json:"-"`
|
||||
CanReviewAnswer bool `json:"-"`
|
||||
CanReviewTag bool `json:"-"`
|
||||
}
|
||||
|
||||
type RevisionSearch struct {
|
||||
Page int `json:"page" form:"page"` // Query number of pages
|
||||
CanReviewQuestion bool `json:"-"`
|
||||
CanReviewAnswer bool `json:"-"`
|
||||
CanReviewTag bool `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (r RevisionSearch) GetCanReviewObjectTypes() []int {
|
||||
objectType := make([]int, 0)
|
||||
if r.CanReviewAnswer {
|
||||
objectType = append(objectType, constant.ObjectTypeStrMapping[constant.AnswerObjectType])
|
||||
}
|
||||
if r.CanReviewQuestion {
|
||||
objectType = append(objectType, constant.ObjectTypeStrMapping[constant.QuestionObjectType])
|
||||
}
|
||||
if r.CanReviewTag {
|
||||
objectType = append(objectType, constant.ObjectTypeStrMapping[constant.TagObjectType])
|
||||
}
|
||||
return objectType
|
||||
}
|
||||
|
||||
type GetUnreviewedRevisionResp struct {
|
||||
Type string `json:"type"`
|
||||
Info *UnreviewedRevisionInfoInfo `json:"info"`
|
||||
UnreviewedInfo *GetRevisionResp `json:"unreviewed_info"`
|
||||
}
|
||||
|
||||
// GetRevisionResp get revision response
|
||||
type GetRevisionResp struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"use_id"`
|
||||
ObjectID string `json:"object_id"`
|
||||
ObjectType int `json:"-"`
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
Content string `json:"-"`
|
||||
ContentParsed any `json:"content"`
|
||||
Status int `json:"status"`
|
||||
CreatedAt time.Time `json:"-"`
|
||||
CreatedAtParsed int64 `json:"create_at"`
|
||||
UserInfo UserBasicInfo `json:"user_info"`
|
||||
Log string `json:"reason"`
|
||||
}
|
||||
|
||||
// GetReviewingTypeReq get reviewing type request
|
||||
type GetReviewingTypeReq struct {
|
||||
CanReviewQuestion bool `json:"-"`
|
||||
CanReviewAnswer bool `json:"-"`
|
||||
CanReviewTag bool `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (r *GetReviewingTypeReq) GetCanReviewObjectTypes() []int {
|
||||
objectType := make([]int, 0)
|
||||
if r.CanReviewAnswer {
|
||||
objectType = append(objectType, constant.ObjectTypeStrMapping[constant.AnswerObjectType])
|
||||
}
|
||||
if r.CanReviewQuestion {
|
||||
objectType = append(objectType, constant.ObjectTypeStrMapping[constant.QuestionObjectType])
|
||||
}
|
||||
if r.CanReviewTag {
|
||||
objectType = append(objectType, constant.ObjectTypeStrMapping[constant.TagObjectType])
|
||||
}
|
||||
return objectType
|
||||
}
|
||||
|
||||
// GetReviewingTypeResp get reviewing type response
|
||||
type GetReviewingTypeResp struct {
|
||||
Name string `json:"name"`
|
||||
Label string `json:"label"`
|
||||
TodoAmount int64 `json:"todo_amount"`
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// GetRoleResp get role response
|
||||
type GetRoleResp struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/apache/answer/plugin"
|
||||
)
|
||||
|
||||
type SearchDTO struct {
|
||||
Query string `validate:"required,gte=1,lte=60" form:"q"`
|
||||
Page int `validate:"omitempty,min=1" form:"page,default=1"`
|
||||
Size int `validate:"omitempty,min=1,max=50" form:"size,default=30"`
|
||||
Order string `validate:"required,oneof=newest active score relevance" form:"order,default=relevance" enums:"newest,active,score,relevance"`
|
||||
CaptchaID string `form:"captcha_id"`
|
||||
CaptchaCode string `form:"captcha_code"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (s *SearchDTO) Check() (errField []*validator.FormErrorField, err error) {
|
||||
// Replace special characters.
|
||||
// Special characters will cause the search abnormal, such as search for "#" will get nearly all the content that Markdown format.
|
||||
replacedContent, patterns := ReplaceSearchContent(s.Query)
|
||||
s.Query = strings.Join(append(patterns, replacedContent), " ")
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func ReplaceSearchContent(content string) (string, []string) {
|
||||
// Define the regular expressions for key:value pairs and [tag]
|
||||
keyValueRegex := regexp.MustCompile(`\w+:\S+`)
|
||||
tagRegex := regexp.MustCompile(`\[\w+\]`)
|
||||
// Define the pattern for characters to replace
|
||||
replaceCharsPattern := regexp.MustCompile(`[+#.<>\-_()*]`)
|
||||
|
||||
// Extract key:value pairs
|
||||
keyValues := keyValueRegex.FindAllString(content, -1)
|
||||
// Extract [tag]
|
||||
tags := tagRegex.FindAllString(content, -1)
|
||||
|
||||
// Replace key:value pairs and [tag] with empty string
|
||||
contentWithoutPatterns := keyValueRegex.ReplaceAllString(content, "")
|
||||
contentWithoutPatterns = tagRegex.ReplaceAllString(contentWithoutPatterns, "")
|
||||
|
||||
// Replace characters with pattern [+#.<>_()*] with space
|
||||
replacedContent := replaceCharsPattern.ReplaceAllString(contentWithoutPatterns, " ")
|
||||
|
||||
return strings.TrimSpace(replacedContent), append(keyValues, tags...)
|
||||
}
|
||||
|
||||
type SearchCondition struct {
|
||||
// search target type: all/question/answer
|
||||
TargetType string
|
||||
// search query user id
|
||||
UserID string
|
||||
// vote amount
|
||||
VoteAmount int
|
||||
// only show not accepted answer's question
|
||||
NotAccepted bool
|
||||
// view amount
|
||||
Views int
|
||||
// answer count
|
||||
AnswerAmount int
|
||||
// only show accepted answer
|
||||
Accepted bool
|
||||
// only show this question's answer
|
||||
QuestionID string
|
||||
// search query tags
|
||||
Tags [][]string
|
||||
// search query keywords
|
||||
Words []string
|
||||
}
|
||||
|
||||
// SearchAll check if search all
|
||||
func (s *SearchCondition) SearchAll() bool {
|
||||
return len(s.TargetType) == 0
|
||||
}
|
||||
|
||||
// SearchQuestion check if search only need question
|
||||
func (s *SearchCondition) SearchQuestion() bool {
|
||||
return s.TargetType == constant.QuestionObjectType
|
||||
}
|
||||
|
||||
// SearchAnswer check if search only need answer
|
||||
func (s *SearchCondition) SearchAnswer() bool {
|
||||
return s.TargetType == constant.AnswerObjectType
|
||||
}
|
||||
|
||||
// Convert2PluginSearchCond convert to plugin search condition
|
||||
func (s *SearchCondition) Convert2PluginSearchCond(page, pageSize int, order string) *plugin.SearchBasicCond {
|
||||
basic := &plugin.SearchBasicCond{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Words: s.Words,
|
||||
TagIDs: s.Tags,
|
||||
UserID: s.UserID,
|
||||
Order: plugin.SearchOrderCond(order),
|
||||
QuestionID: s.QuestionID,
|
||||
VoteAmount: s.VoteAmount,
|
||||
ViewAmount: s.Views,
|
||||
AnswerAmount: s.AnswerAmount,
|
||||
}
|
||||
if s.Accepted {
|
||||
basic.AnswerAccepted = plugin.AcceptedCondTrue
|
||||
} else {
|
||||
basic.AnswerAccepted = plugin.AcceptedCondAll
|
||||
}
|
||||
if s.NotAccepted {
|
||||
basic.QuestionAccepted = plugin.AcceptedCondFalse
|
||||
} else {
|
||||
basic.QuestionAccepted = plugin.AcceptedCondAll
|
||||
}
|
||||
return basic
|
||||
}
|
||||
|
||||
type SearchObject struct {
|
||||
ID string `json:"id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
Excerpt string `json:"excerpt"`
|
||||
CreatedAtParsed int64 `json:"created_at"`
|
||||
VoteCount int `json:"vote_count"`
|
||||
Accepted bool `json:"accepted"`
|
||||
AnswerCount int `json:"answer_count"`
|
||||
// user info
|
||||
UserInfo *SearchObjectUser `json:"user_info"`
|
||||
// tags
|
||||
Tags []*TagResp `json:"tags"`
|
||||
// Status
|
||||
StatusStr string `json:"status"`
|
||||
}
|
||||
|
||||
type SearchObjectUser struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Rank int `json:"rank"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type TagResp struct {
|
||||
ID string `json:"-"`
|
||||
SlugName string `json:"slug_name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
// if main tag slug name is not empty, this tag is synonymous with the main tag
|
||||
MainTagSlugName string `json:"main_tag_slug_name"`
|
||||
Recommend bool `json:"recommend"`
|
||||
Reserved bool `json:"reserved"`
|
||||
}
|
||||
|
||||
type SearchResult struct {
|
||||
// object_type
|
||||
ObjectType string `json:"object_type"`
|
||||
// this object
|
||||
Object *SearchObject `json:"object"`
|
||||
}
|
||||
|
||||
type SearchResp struct {
|
||||
Total int64 `json:"count"`
|
||||
// search response
|
||||
SearchResults []*SearchResult `json:"list"`
|
||||
}
|
||||
|
||||
type SearchDescResp struct {
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReplaceSearchContent(t *testing.T) {
|
||||
content := "user:aaa [tag] ssssfdfdf-as#fsadf"
|
||||
replacedContent, patterns := ReplaceSearchContent(content)
|
||||
ret := strings.Join(append(patterns, replacedContent), " ")
|
||||
|
||||
assert.Equal(t, "user:aaa [tag] ssssfdfdf as fsadf", ret)
|
||||
|
||||
content = "user:aaa-sss [tag1] ssssfdfdf-as#fsadf [tag2] score:3"
|
||||
replacedContent, patterns = ReplaceSearchContent(content)
|
||||
ret = strings.Join(append(patterns, replacedContent), " ")
|
||||
|
||||
assert.Equal(t, "user:aaa-sss score:3 [tag1] [tag2] ssssfdfdf as fsadf", ret)
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/apache/answer/internal/entity"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// SimpleObjectInfo simple object info
|
||||
type SimpleObjectInfo struct {
|
||||
ObjectID string `json:"object_id"`
|
||||
ObjectCreatorUserID string `json:"object_creator_user_id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
QuestionCreatorUserID string `json:"question_creator_user_id"`
|
||||
QuestionStatus int `json:"question_status"`
|
||||
QuestionShow int `json:"question_show"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
AnswerStatus int `json:"answer_status"`
|
||||
CommentID string `json:"comment_id"`
|
||||
CommentStatus int `json:"comment_status"`
|
||||
TagID string `json:"tag_id"`
|
||||
TagStatus int `json:"tag_status"`
|
||||
ObjectType string `json:"object_type"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// IsDeleted is deleted
|
||||
func (s *SimpleObjectInfo) IsDeleted() bool {
|
||||
switch s.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
return s.QuestionStatus == entity.QuestionStatusDeleted
|
||||
case constant.AnswerObjectType:
|
||||
return s.AnswerStatus == entity.AnswerStatusDeleted
|
||||
case constant.CommentObjectType:
|
||||
return s.CommentStatus == entity.CommentStatusDeleted
|
||||
case constant.TagObjectType:
|
||||
return s.TagStatus == entity.TagStatusDeleted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *SimpleObjectInfo) CheckVisibility(userID string, isAdminModerator bool) error {
|
||||
if s == nil {
|
||||
return errors.NotFound(reason.ObjectNotFound)
|
||||
}
|
||||
if s.isObjectRestricted() && !s.canViewObject(userID, isAdminModerator) {
|
||||
return errors.NotFound(s.objectNotFoundReason())
|
||||
}
|
||||
if s.hasParentQuestion() && s.isParentQuestionRestricted() &&
|
||||
!s.canViewParentQuestion(userID, isAdminModerator) {
|
||||
return errors.NotFound(reason.QuestionNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SimpleObjectInfo) canViewObject(userID string, isAdminModerator bool) bool {
|
||||
if isAdminModerator {
|
||||
return true
|
||||
}
|
||||
switch s.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
return s.QuestionCreatorUserID == userID
|
||||
case constant.AnswerObjectType, constant.CommentObjectType, constant.TagObjectType:
|
||||
return s.ObjectCreatorUserID == userID
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleObjectInfo) canViewParentQuestion(userID string, isAdminModerator bool) bool {
|
||||
if isAdminModerator {
|
||||
return true
|
||||
}
|
||||
return s.QuestionCreatorUserID == userID
|
||||
}
|
||||
|
||||
func (s *SimpleObjectInfo) hasParentQuestion() bool {
|
||||
switch s.ObjectType {
|
||||
case constant.AnswerObjectType, constant.CommentObjectType:
|
||||
return len(s.QuestionID) > 0 && s.QuestionID != "0"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleObjectInfo) isObjectRestricted() bool {
|
||||
switch s.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
return s.QuestionStatus == entity.QuestionStatusDeleted ||
|
||||
s.QuestionStatus == entity.QuestionStatusPending ||
|
||||
s.QuestionShow == entity.QuestionHide
|
||||
case constant.AnswerObjectType:
|
||||
return s.AnswerStatus == entity.AnswerStatusDeleted || s.AnswerStatus == entity.AnswerStatusPending
|
||||
case constant.CommentObjectType:
|
||||
return s.CommentStatus == entity.CommentStatusDeleted || s.CommentStatus == entity.CommentStatusPending
|
||||
case constant.TagObjectType:
|
||||
return s.TagStatus == entity.TagStatusDeleted
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleObjectInfo) isParentQuestionRestricted() bool {
|
||||
return s.QuestionStatus == entity.QuestionStatusDeleted ||
|
||||
s.QuestionStatus == entity.QuestionStatusPending ||
|
||||
s.QuestionShow == entity.QuestionHide
|
||||
}
|
||||
|
||||
func (s *SimpleObjectInfo) objectNotFoundReason() string {
|
||||
switch s.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
return reason.QuestionNotFound
|
||||
case constant.AnswerObjectType:
|
||||
return reason.AnswerNotFound
|
||||
case constant.CommentObjectType:
|
||||
return reason.CommentNotFound
|
||||
case constant.TagObjectType:
|
||||
return reason.TagNotFound
|
||||
default:
|
||||
return reason.ObjectNotFound
|
||||
}
|
||||
}
|
||||
|
||||
type UnreviewedRevisionInfoInfo struct {
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
ObjectID string `json:"object_id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
AnswerID string `json:"answer_id"`
|
||||
CommentID string `json:"comment_id"`
|
||||
ObjectType string `json:"object_type"`
|
||||
ObjectCreatorUserID string `json:"object_creator_user_id"`
|
||||
Title string `json:"title"`
|
||||
UrlTitle string `json:"url_title"`
|
||||
Content string `json:"content"`
|
||||
Html string `json:"html"`
|
||||
AnswerCount int `json:"answer_count"`
|
||||
AnswerAccepted bool `json:"answer_accepted"`
|
||||
Tags []*TagResp `json:"tags"`
|
||||
Status int `json:"status"`
|
||||
ShowStatus int `json:"show_status"`
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/internal/base/handler"
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/apache/answer/internal/base/translator"
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// SiteGeneralReq site general request
|
||||
type SiteGeneralReq struct {
|
||||
Name string `validate:"required,sanitizer,gt=1,lte=128" form:"name" json:"name"`
|
||||
ShortDescription string `validate:"omitempty,sanitizer,gt=3,lte=255" form:"short_description" json:"short_description"`
|
||||
Description string `validate:"omitempty,sanitizer,gt=3,lte=2000" form:"description" json:"description"`
|
||||
SiteUrl string `validate:"required,sanitizer,gt=1,lte=512,url" form:"site_url" json:"site_url"`
|
||||
ContactEmail string `validate:"required,sanitizer,gt=1,lte=512,email" form:"contact_email" json:"contact_email"`
|
||||
}
|
||||
|
||||
func (r *SiteGeneralReq) FormatSiteUrl() {
|
||||
parsedUrl, err := url.Parse(r.SiteUrl)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r.SiteUrl = fmt.Sprintf("%s://%s", parsedUrl.Scheme, parsedUrl.Host)
|
||||
if len(parsedUrl.Path) > 0 {
|
||||
r.SiteUrl += parsedUrl.Path
|
||||
r.SiteUrl = strings.TrimSuffix(r.SiteUrl, "/")
|
||||
}
|
||||
}
|
||||
|
||||
// SiteInterfaceReq site interface request
|
||||
type SiteInterfaceReq struct {
|
||||
Language string `validate:"required,gt=1,lte=128" form:"language" json:"language"`
|
||||
TimeZone string `validate:"required,gt=1,lte=128" form:"time_zone" json:"time_zone"`
|
||||
// Deperecated: use SiteUsersSettingsReq instead
|
||||
DefaultAvatar string `validate:"omitempty" json:"-"`
|
||||
// Deperecated: use SiteUsersSettingsReq instead
|
||||
GravatarBaseURL string `validate:"omitempty" json:"-"`
|
||||
}
|
||||
|
||||
// SiteInterfaceSettingsReq site interface settings request
|
||||
type SiteInterfaceSettingsReq struct {
|
||||
Language string `validate:"required,gt=1,lte=128" json:"language"`
|
||||
TimeZone string `validate:"required,gt=1,lte=128" json:"time_zone"`
|
||||
}
|
||||
|
||||
type SiteInterfaceSettingsResp SiteInterfaceSettingsReq
|
||||
|
||||
type SiteUsersSettingsReq struct {
|
||||
DefaultAvatar string `validate:"required,oneof=system gravatar" json:"default_avatar"`
|
||||
GravatarBaseURL string `validate:"omitempty" json:"gravatar_base_url"`
|
||||
}
|
||||
|
||||
type SiteUsersSettingsResp SiteUsersSettingsReq
|
||||
|
||||
// SiteBrandingReq site branding request
|
||||
type SiteBrandingReq struct {
|
||||
Logo string `validate:"omitempty,gt=0,lte=512" form:"logo" json:"logo"`
|
||||
MobileLogo string `validate:"omitempty,gt=0,lte=512" form:"mobile_logo" json:"mobile_logo"`
|
||||
SquareIcon string `validate:"omitempty,gt=0,lte=512" form:"square_icon" json:"square_icon"`
|
||||
Favicon string `validate:"omitempty,gt=0,lte=512" form:"favicon" json:"favicon"`
|
||||
}
|
||||
|
||||
// SiteWriteReq site write request use SiteQuestionsReq, SiteAdvancedReq and SiteTagsReq instead
|
||||
type SiteWriteReq struct {
|
||||
MinimumContent int `validate:"omitempty,gte=0,lte=65535" json:"min_content"`
|
||||
RestrictAnswer bool `validate:"omitempty" json:"restrict_answer"`
|
||||
MinimumTags int `validate:"omitempty,gte=0,lte=5" json:"min_tags"`
|
||||
RequiredTag bool `validate:"omitempty" json:"required_tag"`
|
||||
RecommendTags []*SiteWriteTag `validate:"omitempty,dive" json:"recommend_tags"`
|
||||
ReservedTags []*SiteWriteTag `validate:"omitempty,dive" json:"reserved_tags"`
|
||||
MaxImageSize int `validate:"omitempty,gt=0" json:"max_image_size"`
|
||||
MaxAttachmentSize int `validate:"omitempty,gt=0" json:"max_attachment_size"`
|
||||
MaxImageMegapixel int `validate:"omitempty,gt=0" json:"max_image_megapixel"`
|
||||
AuthorizedImageExtensions []string `validate:"omitempty" json:"authorized_image_extensions"`
|
||||
AuthorizedAttachmentExtensions []string `validate:"omitempty" json:"authorized_attachment_extensions"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type SiteWriteResp SiteWriteReq
|
||||
|
||||
// SiteQuestionsReq site questions settings request
|
||||
type SiteQuestionsReq struct {
|
||||
MinimumTags int `validate:"omitempty,gte=0,lte=5" json:"min_tags"`
|
||||
MinimumContent int `validate:"omitempty,gte=0,lte=65535" json:"min_content"`
|
||||
RestrictAnswer bool `validate:"omitempty" json:"restrict_answer"`
|
||||
}
|
||||
|
||||
// SiteAdvancedReq site advanced settings request
|
||||
type SiteAdvancedReq struct {
|
||||
MaxImageSize int `validate:"omitempty,gt=0" json:"max_image_size"`
|
||||
MaxAttachmentSize int `validate:"omitempty,gt=0" json:"max_attachment_size"`
|
||||
MaxImageMegapixel int `validate:"omitempty,gt=0" json:"max_image_megapixel"`
|
||||
AuthorizedImageExtensions []string `validate:"omitempty" json:"authorized_image_extensions"`
|
||||
AuthorizedAttachmentExtensions []string `validate:"omitempty" json:"authorized_attachment_extensions"`
|
||||
}
|
||||
|
||||
// SiteTagsReq site tags settings request
|
||||
type SiteTagsReq struct {
|
||||
ReservedTags []*SiteWriteTag `validate:"omitempty,dive" json:"reserved_tags"`
|
||||
RecommendTags []*SiteWriteTag `validate:"omitempty,dive" json:"recommend_tags"`
|
||||
RequiredTag bool `validate:"omitempty" json:"required_tag"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (s *SiteAdvancedResp) GetMaxImageSize() int64 {
|
||||
if s.MaxImageSize <= 0 {
|
||||
return constant.DefaultMaxImageSize
|
||||
}
|
||||
return int64(s.MaxImageSize) * 1024 * 1024
|
||||
}
|
||||
|
||||
func (s *SiteAdvancedResp) GetMaxAttachmentSize() int64 {
|
||||
if s.MaxAttachmentSize <= 0 {
|
||||
return constant.DefaultMaxAttachmentSize
|
||||
}
|
||||
return int64(s.MaxAttachmentSize) * 1024 * 1024
|
||||
}
|
||||
|
||||
func (s *SiteAdvancedResp) GetMaxImageMegapixel() int {
|
||||
if s.MaxImageMegapixel <= 0 {
|
||||
return constant.DefaultMaxImageMegapixel
|
||||
}
|
||||
return s.MaxImageMegapixel * 1000 * 1000
|
||||
}
|
||||
|
||||
// SiteWriteTag site write response tag
|
||||
type SiteWriteTag struct {
|
||||
SlugName string `validate:"required" json:"slug_name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
}
|
||||
|
||||
// SiteLegalReq site branding request use SitePoliciesReq and SiteSecurityReq instead
|
||||
type SiteLegalReq struct {
|
||||
TermsOfServiceOriginalText string `json:"terms_of_service_original_text"`
|
||||
TermsOfServiceParsedText string `json:"terms_of_service_parsed_text"`
|
||||
PrivacyPolicyOriginalText string `json:"privacy_policy_original_text"`
|
||||
PrivacyPolicyParsedText string `json:"privacy_policy_parsed_text"`
|
||||
ExternalContentDisplay string `validate:"required,oneof=always_display ask_before_display" json:"external_content_display"`
|
||||
}
|
||||
|
||||
type SitePoliciesReq struct {
|
||||
TermsOfServiceOriginalText string `json:"terms_of_service_original_text"`
|
||||
TermsOfServiceParsedText string `json:"terms_of_service_parsed_text"`
|
||||
PrivacyPolicyOriginalText string `json:"privacy_policy_original_text"`
|
||||
PrivacyPolicyParsedText string `json:"privacy_policy_parsed_text"`
|
||||
}
|
||||
|
||||
type SiteSecurityReq struct {
|
||||
LoginRequired bool `json:"login_required"`
|
||||
ExternalContentDisplay string `validate:"required,oneof=always_display ask_before_display" json:"external_content_display"`
|
||||
CheckUpdate bool `validate:"omitempty,sanitizer" form:"check_update" json:"check_update"`
|
||||
}
|
||||
|
||||
type SitePoliciesResp SitePoliciesReq
|
||||
type SiteSecurityResp SiteSecurityReq
|
||||
|
||||
// GetSiteLegalInfoReq site site legal request
|
||||
type GetSiteLegalInfoReq struct {
|
||||
InfoType string `validate:"required,oneof=tos privacy" form:"info_type"`
|
||||
}
|
||||
|
||||
func (r *GetSiteLegalInfoReq) IsTOS() bool {
|
||||
return r.InfoType == "tos"
|
||||
}
|
||||
|
||||
func (r *GetSiteLegalInfoReq) IsPrivacy() bool {
|
||||
return r.InfoType == "privacy"
|
||||
}
|
||||
|
||||
// GetSiteLegalInfoResp get site legal info response
|
||||
type GetSiteLegalInfoResp struct {
|
||||
TermsOfServiceOriginalText string `json:"terms_of_service_original_text,omitempty"`
|
||||
TermsOfServiceParsedText string `json:"terms_of_service_parsed_text,omitempty"`
|
||||
PrivacyPolicyOriginalText string `json:"privacy_policy_original_text,omitempty"`
|
||||
PrivacyPolicyParsedText string `json:"privacy_policy_parsed_text,omitempty"`
|
||||
}
|
||||
|
||||
// SiteUsersReq site users config request
|
||||
type SiteUsersReq struct {
|
||||
DefaultAvatar string `validate:"required,oneof=system gravatar" json:"default_avatar"`
|
||||
GravatarBaseURL string `json:"gravatar_base_url"`
|
||||
AllowUpdateDisplayName bool `json:"allow_update_display_name"`
|
||||
AllowUpdateUsername bool `json:"allow_update_username"`
|
||||
AllowUpdateAvatar bool `json:"allow_update_avatar"`
|
||||
AllowUpdateBio bool `json:"allow_update_bio"`
|
||||
AllowUpdateWebsite bool `json:"allow_update_website"`
|
||||
AllowUpdateLocation bool `json:"allow_update_location"`
|
||||
}
|
||||
|
||||
// SiteLoginReq site login request
|
||||
type SiteLoginReq struct {
|
||||
AllowNewRegistrations bool `json:"allow_new_registrations"`
|
||||
AllowEmailRegistrations bool `json:"allow_email_registrations"`
|
||||
AllowPasswordLogin bool `json:"allow_password_login"`
|
||||
AllowEmailDomains []string `json:"allow_email_domains"`
|
||||
RequireEmailVerification *bool `validate:"required" json:"require_email_verification" swaggertype:"boolean"`
|
||||
}
|
||||
|
||||
// SiteLoginResp site login response
|
||||
type SiteLoginResp struct {
|
||||
AllowNewRegistrations bool `json:"allow_new_registrations"`
|
||||
AllowEmailRegistrations bool `json:"allow_email_registrations"`
|
||||
AllowPasswordLogin bool `json:"allow_password_login"`
|
||||
AllowEmailDomains []string `json:"allow_email_domains"`
|
||||
RequireEmailVerification bool `json:"require_email_verification"`
|
||||
}
|
||||
|
||||
// SiteCustomCssHTMLReq site custom css html
|
||||
type SiteCustomCssHTMLReq struct {
|
||||
CustomHead string `validate:"omitempty,gt=0,lte=65536" json:"custom_head"`
|
||||
CustomCss string `validate:"omitempty,gt=0,lte=65536" json:"custom_css"`
|
||||
CustomHeader string `validate:"omitempty,gt=0,lte=65536" json:"custom_header"`
|
||||
CustomFooter string `validate:"omitempty,gt=0,lte=65536" json:"custom_footer"`
|
||||
CustomSideBar string `validate:"omitempty,gt=0,lte=65536" json:"custom_sidebar"`
|
||||
}
|
||||
|
||||
// SiteThemeReq site theme config
|
||||
type SiteThemeReq struct {
|
||||
Theme string `validate:"required,gt=0,lte=255" json:"theme"`
|
||||
ThemeConfig map[string]any `validate:"omitempty" json:"theme_config"`
|
||||
ColorScheme string `validate:"omitempty,gt=0,lte=100" json:"color_scheme"`
|
||||
Layout string `validate:"omitempty,oneof=Full-width Fixed-width" json:"layout"`
|
||||
}
|
||||
|
||||
type SiteSeoReq struct {
|
||||
Permalink int `validate:"required,lte=4,gte=0" form:"permalink" json:"permalink"`
|
||||
Robots string `validate:"required" form:"robots" json:"robots"`
|
||||
}
|
||||
|
||||
func (s *SiteSeoResp) IsShortLink() bool {
|
||||
return s.Permalink == constant.PermalinkQuestionIDAndTitleByShortID ||
|
||||
s.Permalink == constant.PermalinkQuestionIDByShortID
|
||||
}
|
||||
|
||||
// AIPromptConfig AI prompt configuration for different languages
|
||||
type AIPromptConfig struct {
|
||||
ZhCN string `json:"zh_cn"`
|
||||
EnUS string `json:"en_us"`
|
||||
}
|
||||
|
||||
// SiteAIReq AI configuration request
|
||||
type SiteAIReq struct {
|
||||
Enabled bool `validate:"omitempty" form:"enabled" json:"enabled"`
|
||||
ChosenProvider string `validate:"omitempty,lte=50" form:"chosen_provider" json:"chosen_provider"`
|
||||
SiteAIProviders []*SiteAIProvider `validate:"omitempty,dive" form:"ai_providers" json:"ai_providers"`
|
||||
PromptConfig *AIPromptConfig `validate:"omitempty" form:"prompt_config" json:"prompt_config,omitempty"`
|
||||
}
|
||||
|
||||
func (s *SiteAIResp) GetProvider() *SiteAIProvider {
|
||||
if !s.Enabled || s.ChosenProvider == "" {
|
||||
return &SiteAIProvider{}
|
||||
}
|
||||
if len(s.SiteAIProviders) == 0 {
|
||||
return &SiteAIProvider{}
|
||||
}
|
||||
for _, provider := range s.SiteAIProviders {
|
||||
if provider.Provider == s.ChosenProvider {
|
||||
return provider
|
||||
}
|
||||
}
|
||||
return &SiteAIProvider{}
|
||||
}
|
||||
|
||||
type SiteAIProvider struct {
|
||||
Provider string `validate:"omitempty,lte=50" form:"provider" json:"provider"`
|
||||
APIHost string `validate:"omitempty,lte=512" form:"api_host" json:"api_host"`
|
||||
APIKey string `validate:"omitempty,lte=256" form:"api_key" json:"api_key"`
|
||||
Model string `validate:"omitempty,lte=100" form:"model" json:"model"`
|
||||
}
|
||||
|
||||
// SiteAIResp AI configuration response
|
||||
type SiteAIResp SiteAIReq
|
||||
|
||||
type SiteMCPReq struct {
|
||||
Enabled bool `validate:"omitempty" form:"enabled" json:"enabled"`
|
||||
}
|
||||
|
||||
type SiteMCPResp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
HTTPHeader string `json:"http_header"`
|
||||
}
|
||||
|
||||
// SiteGeneralResp site general response
|
||||
type SiteGeneralResp SiteGeneralReq
|
||||
|
||||
// SiteInterfaceResp site interface response
|
||||
type SiteInterfaceResp SiteInterfaceReq
|
||||
|
||||
// SiteBrandingResp site branding response
|
||||
type SiteBrandingResp SiteBrandingReq
|
||||
|
||||
// SiteCustomCssHTMLResp site custom css html response
|
||||
type SiteCustomCssHTMLResp SiteCustomCssHTMLReq
|
||||
|
||||
// SiteUsersResp site users response
|
||||
type SiteUsersResp SiteUsersReq
|
||||
|
||||
// SiteThemeResp site theme response
|
||||
type SiteThemeResp struct {
|
||||
ThemeOptions []*ThemeOption `json:"theme_options"`
|
||||
Theme string `json:"theme"`
|
||||
ThemeConfig map[string]any `json:"theme_config"`
|
||||
ColorScheme string `json:"color_scheme"`
|
||||
Layout string `json:"layout"`
|
||||
}
|
||||
|
||||
func (s *SiteThemeResp) TrTheme(ctx context.Context) {
|
||||
la := handler.GetLangByCtx(ctx)
|
||||
for _, option := range s.ThemeOptions {
|
||||
tr := translator.Tr(la, option.Value)
|
||||
// if tr is equal the option value means not found translation, so use the original label
|
||||
if tr != option.Value {
|
||||
option.Label = tr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ThemeOption get label option
|
||||
type ThemeOption struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type SiteQuestionsResp SiteQuestionsReq
|
||||
type SiteAdvancedResp SiteAdvancedReq
|
||||
type SiteTagsResp SiteTagsReq
|
||||
|
||||
// SiteLegalResp site write response use SitePoliciesResp and SiteSecurityResp instead
|
||||
type SiteLegalResp SiteLegalReq
|
||||
|
||||
// SiteLegalSimpleResp site write response
|
||||
type SiteLegalSimpleResp struct {
|
||||
ExternalContentDisplay string `validate:"required,oneof=always_display ask_before_display" json:"external_content_display"`
|
||||
}
|
||||
|
||||
// SiteSeoResp site write response
|
||||
type SiteSeoResp SiteSeoReq
|
||||
|
||||
// SiteInfoResp get site info response
|
||||
type SiteInfoResp struct {
|
||||
General *SiteGeneralResp `json:"general"`
|
||||
Interface *SiteInterfaceSettingsResp `json:"interface"`
|
||||
UsersSettings *SiteUsersSettingsResp `json:"users_settings"`
|
||||
Branding *SiteBrandingResp `json:"branding"`
|
||||
Login *SiteLoginResp `json:"login"`
|
||||
Theme *SiteThemeResp `json:"theme"`
|
||||
CustomCssHtml *SiteCustomCssHTMLResp `json:"custom_css_html"`
|
||||
SiteSeo *SiteSeoResp `json:"site_seo"`
|
||||
SiteUsers *SiteUsersResp `json:"site_users"`
|
||||
Advanced *SiteAdvancedResp `json:"site_advanced"`
|
||||
Questions *SiteQuestionsResp `json:"site_questions"`
|
||||
Tags *SiteTagsResp `json:"site_tags"`
|
||||
Legal *SiteLegalSimpleResp `json:"site_legal"`
|
||||
Security *SiteSecurityResp `json:"site_security"`
|
||||
Version string `json:"version"`
|
||||
Revision string `json:"revision"`
|
||||
AIEnabled bool `json:"ai_enabled"`
|
||||
MCPEnabled bool `json:"mcp_enabled"`
|
||||
}
|
||||
|
||||
type TemplateSiteInfoResp struct {
|
||||
General *SiteGeneralResp `json:"general"`
|
||||
Interface *SiteInterfaceSettingsResp `json:"interface"`
|
||||
Branding *SiteBrandingResp `json:"branding"`
|
||||
SiteSeo *SiteSeoResp `json:"site_seo"`
|
||||
CustomCssHtml *SiteCustomCssHTMLResp `json:"custom_css_html"`
|
||||
Title string
|
||||
Year string
|
||||
Canonical string
|
||||
JsonLD string
|
||||
Keywords string
|
||||
Description string
|
||||
}
|
||||
|
||||
// UpdateSMTPConfigReq get smtp config request
|
||||
type UpdateSMTPConfigReq struct {
|
||||
FromEmail string `validate:"omitempty,gt=0,lte=256" json:"from_email"`
|
||||
FromName string `validate:"omitempty,gt=0,lte=256" json:"from_name"`
|
||||
SMTPHost string `validate:"omitempty,gt=0,lte=256" json:"smtp_host"`
|
||||
SMTPPort int `validate:"omitempty,min=1,max=65535" json:"smtp_port"`
|
||||
Encryption string `validate:"omitempty,oneof=SSL TLS" json:"encryption"` // "" SSL TLS
|
||||
SMTPUsername string `validate:"omitempty,gt=0,lte=256" json:"smtp_username"`
|
||||
SMTPPassword string `validate:"omitempty,gt=0,lte=256" json:"smtp_password"`
|
||||
SMTPAuthentication bool `validate:"omitempty" json:"smtp_authentication"`
|
||||
TestEmailRecipient string `validate:"omitempty,email" json:"test_email_recipient"`
|
||||
}
|
||||
|
||||
func (r *UpdateSMTPConfigReq) Check() (errField []*validator.FormErrorField, err error) {
|
||||
_, err = mail.ParseAddress(r.FromName)
|
||||
if err == nil {
|
||||
return append(errField, &validator.FormErrorField{
|
||||
ErrorField: "from_name",
|
||||
ErrorMsg: reason.SMTPConfigFromNameCannotBeEmail,
|
||||
}), errors.BadRequest(reason.SMTPConfigFromNameCannotBeEmail)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetSMTPConfigResp get smtp config response
|
||||
type GetSMTPConfigResp struct {
|
||||
FromEmail string `json:"from_email"`
|
||||
FromName string `json:"from_name"`
|
||||
SMTPHost string `json:"smtp_host"`
|
||||
SMTPPort int `json:"smtp_port"`
|
||||
Encryption string `json:"encryption"` // "" SSL TLS
|
||||
SMTPUsername string `json:"smtp_username"`
|
||||
SMTPPassword string `json:"smtp_password"`
|
||||
SMTPAuthentication bool `json:"smtp_authentication"`
|
||||
}
|
||||
|
||||
// GetManifestJsonResp get manifest json response
|
||||
type GetManifestJsonResp struct {
|
||||
ManifestVersion int `json:"manifest_version"`
|
||||
Version string `json:"version"`
|
||||
Revision string `json:"revision"`
|
||||
ShortName string `json:"short_name"`
|
||||
Name string `json:"name"`
|
||||
Icons []ManifestJsonIcon `json:"icons"`
|
||||
StartUrl string `json:"start_url"`
|
||||
Display string `json:"display"`
|
||||
ThemeColor string `json:"theme_color"`
|
||||
BackgroundColor string `json:"background_color"`
|
||||
}
|
||||
|
||||
type ManifestJsonIcon struct {
|
||||
Src string `json:"src"`
|
||||
Sizes string `json:"sizes"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func CreateManifestJsonIcons(icon string) []ManifestJsonIcon {
|
||||
ext := filepath.Ext(icon)
|
||||
if ext == "" {
|
||||
ext = "png"
|
||||
} else {
|
||||
ext = strings.ToLower(ext[1:])
|
||||
}
|
||||
iconType := fmt.Sprintf("image/%s", ext)
|
||||
return []ManifestJsonIcon{
|
||||
{
|
||||
Src: icon,
|
||||
Sizes: "16x16",
|
||||
Type: iconType,
|
||||
},
|
||||
{
|
||||
Src: icon,
|
||||
Sizes: "32x32",
|
||||
Type: iconType,
|
||||
},
|
||||
{
|
||||
Src: icon,
|
||||
Sizes: "48x48",
|
||||
Type: iconType,
|
||||
},
|
||||
{
|
||||
Src: icon,
|
||||
Sizes: "128x128",
|
||||
Type: iconType,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// PrivilegeLevel1 low
|
||||
PrivilegeLevel1 PrivilegeLevel = 1
|
||||
// PrivilegeLevel2 medium
|
||||
PrivilegeLevel2 PrivilegeLevel = 2
|
||||
// PrivilegeLevel3 high
|
||||
PrivilegeLevel3 PrivilegeLevel = 3
|
||||
// PrivilegeLevelCustom custom
|
||||
PrivilegeLevelCustom PrivilegeLevel = 99
|
||||
)
|
||||
|
||||
type PrivilegeLevel int
|
||||
type PrivilegeOptions []*PrivilegeOption
|
||||
|
||||
func (p PrivilegeOptions) Choose(level PrivilegeLevel) (option *PrivilegeOption) {
|
||||
for _, op := range p {
|
||||
if op.Level == level {
|
||||
return op
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPrivilegesConfigResp get privileges config response
|
||||
type GetPrivilegesConfigResp struct {
|
||||
Options []*PrivilegeOption `json:"options"`
|
||||
SelectedLevel PrivilegeLevel `json:"selected_level"`
|
||||
}
|
||||
|
||||
// PrivilegeOption privilege option
|
||||
type PrivilegeOption struct {
|
||||
Level PrivilegeLevel `json:"level"`
|
||||
LevelDesc string `json:"level_desc"`
|
||||
Privileges []*constant.Privilege `validate:"dive" json:"privileges"`
|
||||
}
|
||||
|
||||
// UpdatePrivilegesConfigReq update privileges config request
|
||||
type UpdatePrivilegesConfigReq struct {
|
||||
Level PrivilegeLevel `validate:"required,min=1,max=3|eq=99" json:"level"`
|
||||
CustomPrivileges []*constant.Privilege `validate:"dive" json:"custom_privileges"`
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultPrivilegeOptions PrivilegeOptions
|
||||
DefaultCustomPrivilegeOption *PrivilegeOption
|
||||
privilegeOptionsLevelMapping = map[string][]int{
|
||||
constant.RankQuestionAddKey: {1, 1, 1},
|
||||
constant.RankAnswerAddKey: {1, 1, 1},
|
||||
constant.RankCommentAddKey: {1, 1, 1},
|
||||
constant.RankReportAddKey: {1, 1, 1},
|
||||
constant.RankCommentVoteUpKey: {1, 1, 1},
|
||||
constant.RankLinkUrlLimitKey: {1, 10, 10},
|
||||
constant.RankQuestionVoteUpKey: {1, 8, 15},
|
||||
constant.RankAnswerVoteUpKey: {1, 8, 15},
|
||||
constant.RankQuestionVoteDownKey: {125, 125, 125},
|
||||
constant.RankAnswerVoteDownKey: {125, 125, 125},
|
||||
constant.RankInviteSomeoneToAnswerKey: {1, 500, 1000},
|
||||
constant.RankTagAddKey: {1, 750, 1500},
|
||||
constant.RankTagEditKey: {1, 50, 100},
|
||||
constant.RankQuestionEditKey: {1, 100, 200},
|
||||
constant.RankAnswerEditKey: {1, 100, 200},
|
||||
constant.RankQuestionEditWithoutReviewKey: {1, 1000, 2000},
|
||||
constant.RankAnswerEditWithoutReviewKey: {1, 1000, 2000},
|
||||
constant.RankQuestionAuditKey: {1, 1000, 2000},
|
||||
constant.RankAnswerAuditKey: {1, 1000, 2000},
|
||||
constant.RankTagAuditKey: {1, 2500, 5000},
|
||||
constant.RankTagEditWithoutReviewKey: {1, 10000, 20000},
|
||||
constant.RankTagSynonymKey: {1, 10000, 20000},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
DefaultPrivilegeOptions = append(DefaultPrivilegeOptions, &PrivilegeOption{
|
||||
Level: PrivilegeLevel1,
|
||||
LevelDesc: reason.PrivilegeLevel1Desc,
|
||||
}, &PrivilegeOption{
|
||||
Level: PrivilegeLevel2,
|
||||
LevelDesc: reason.PrivilegeLevel2Desc,
|
||||
}, &PrivilegeOption{
|
||||
Level: PrivilegeLevel3,
|
||||
LevelDesc: reason.PrivilegeLevel3Desc,
|
||||
})
|
||||
|
||||
for _, option := range DefaultPrivilegeOptions {
|
||||
for _, privilege := range constant.RankAllPrivileges {
|
||||
if len(privilegeOptionsLevelMapping[privilege.Key]) == 0 {
|
||||
continue
|
||||
}
|
||||
option.Privileges = append(option.Privileges, &constant.Privilege{
|
||||
Label: privilege.Label,
|
||||
Value: privilegeOptionsLevelMapping[privilege.Key][option.Level-1],
|
||||
Key: privilege.Key,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// set up default custom privilege option
|
||||
DefaultCustomPrivilegeOption = &PrivilegeOption{
|
||||
Level: PrivilegeLevelCustom,
|
||||
LevelDesc: reason.PrivilegeLevelCustomDesc,
|
||||
Privileges: DefaultPrivilegeOptions[0].Privileges,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSiteLoginReqRequireEmailVerificationValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
payload string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "omitted is invalid",
|
||||
payload: `{"allow_new_registrations":true,"allow_email_registrations":true,"allow_password_login":true,"allow_email_domains":[]}`,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "null is invalid",
|
||||
payload: `{"allow_new_registrations":true,"allow_email_registrations":true,"allow_password_login":true,"allow_email_domains":[],"require_email_verification":null}`,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "false is valid",
|
||||
payload: `{"allow_new_registrations":true,"allow_email_registrations":true,"allow_password_login":true,"allow_email_domains":[],"require_email_verification":false}`,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "true is valid",
|
||||
payload: `{"allow_new_registrations":true,"allow_email_registrations":true,"allow_password_login":true,"allow_email_domains":[],"require_email_verification":true}`,
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := &SiteLoginReq{}
|
||||
require.NoError(t, json.Unmarshal([]byte(tt.payload), req))
|
||||
|
||||
_, err := validator.GetValidatorByLang(i18n.DefaultLanguage).Check(req)
|
||||
if tt.expectError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
type SiteMapList struct {
|
||||
QuestionIDs []*SiteMapQuestionInfo `json:"question_ids"`
|
||||
MaxPageNum []int `json:"max_page_num"`
|
||||
}
|
||||
|
||||
type SiteMapPageList struct {
|
||||
PageData []*SiteMapQuestionInfo `json:"page_data"`
|
||||
}
|
||||
|
||||
type SiteMapQuestionInfo struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
UpdateTime string `json:"time"`
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
// AddTagListReq add tag list request
|
||||
type AddTagListReq struct {
|
||||
// tag_id
|
||||
TagID int64 `validate:"required" comment:"tag_id" json:"tag_id"`
|
||||
// object_id
|
||||
ObjectID int64 `validate:"required" comment:"object_id" json:"object_id"`
|
||||
// tag_list_status(available: 1; deleted: 10)
|
||||
Status int `validate:"required" comment:"tag_list_status(available: 1; deleted: 10)" json:"status"`
|
||||
}
|
||||
|
||||
// RemoveTagListReq delete tag list request
|
||||
type RemoveTagListReq struct {
|
||||
// tag_list_id
|
||||
ID int64 `validate:"required" comment:"tag_list_id" json:"id"`
|
||||
}
|
||||
|
||||
// UpdateTagListReq update tag list request
|
||||
type UpdateTagListReq struct {
|
||||
// tag_list_id
|
||||
ID int64 `validate:"required" comment:"tag_list_id" json:"id"`
|
||||
// tag_id
|
||||
TagID int64 `validate:"omitempty" comment:"tag_id" json:"tag_id"`
|
||||
// object_id
|
||||
ObjectID int64 `validate:"omitempty" comment:"object_id" json:"object_id"`
|
||||
// tag_list_status(available: 1; deleted: 10)
|
||||
Status int `validate:"omitempty" comment:"tag_list_status(available: 1; deleted: 10)" json:"status"`
|
||||
}
|
||||
|
||||
// GetTagListListReq get tag list list all request
|
||||
type GetTagListListReq struct {
|
||||
// tag_id
|
||||
TagID int64 `validate:"omitempty" comment:"tag_id" form:"tag_id"`
|
||||
// object_id
|
||||
ObjectID int64 `validate:"omitempty" comment:"object_id" form:"object_id"`
|
||||
// tag_list_status(available: 1; deleted: 10)
|
||||
Status int `validate:"omitempty" comment:"tag_list_status(available: 1; deleted: 10)" form:"status"`
|
||||
}
|
||||
|
||||
// GetTagListWithPageReq get tag list list page request
|
||||
type GetTagListWithPageReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// tag_id
|
||||
TagID int64 `validate:"omitempty" comment:"tag_id" form:"tag_id"`
|
||||
// object_id
|
||||
ObjectID int64 `validate:"omitempty" comment:"object_id" form:"object_id"`
|
||||
// tag_list_status(available: 1; deleted: 10)
|
||||
Status int `validate:"omitempty" comment:"tag_list_status(available: 1; deleted: 10)" form:"status"`
|
||||
}
|
||||
|
||||
// GetTagListResp get tag list response
|
||||
type GetTagListResp struct {
|
||||
// tag_list_id
|
||||
ID int64 `json:"id"`
|
||||
// tag_id
|
||||
TagID int64 `json:"tag_id"`
|
||||
// object_id
|
||||
ObjectID int64 `json:"object_id"`
|
||||
// tag_list_status(available: 1; deleted: 10)
|
||||
Status int `json:"status"`
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/apache/answer/pkg/converter"
|
||||
)
|
||||
|
||||
// SearchTagLikeReq get tag list all request
|
||||
type SearchTagLikeReq struct {
|
||||
// tag
|
||||
Tag string `validate:"omitempty" form:"tag"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
// SearchTagsBySlugName search tags by slug name
|
||||
type SearchTagsBySlugName struct {
|
||||
// slug name list split by ','
|
||||
Tags string `form:"tags"`
|
||||
}
|
||||
|
||||
// GetTagInfoReq get tag info request
|
||||
type GetTagInfoReq struct {
|
||||
// tag id
|
||||
ID string `validate:"omitempty" form:"id"`
|
||||
// tag slug name
|
||||
Name string `validate:"omitempty,gt=0,lte=35" form:"name"`
|
||||
UserID string `json:"-"`
|
||||
CanEdit bool `json:"-"`
|
||||
CanDelete bool `json:"-"`
|
||||
CanMerge bool `json:"-"`
|
||||
CanRecover bool `json:"-"`
|
||||
}
|
||||
|
||||
type GetTamplateTagInfoReq struct {
|
||||
// tag id
|
||||
ID string `validate:"omitempty" form:"id"`
|
||||
// tag slug name
|
||||
Name string `validate:"omitempty" form:"name"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
}
|
||||
|
||||
func (r *GetTagInfoReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
r.Name = strings.ToLower(r.Name)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetTagResp get tag response
|
||||
type GetTagResp struct {
|
||||
TagID string `json:"tag_id"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
SlugName string `json:"slug_name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Excerpt string `json:"excerpt"`
|
||||
OriginalText string `json:"original_text"`
|
||||
ParsedText string `json:"parsed_text"`
|
||||
Description string `json:"description"`
|
||||
FollowCount int `json:"follow_count"`
|
||||
QuestionCount int `json:"question_count"`
|
||||
IsFollower bool `json:"is_follower"`
|
||||
Status string `json:"status"`
|
||||
MemberActions []*PermissionMemberAction `json:"member_actions"`
|
||||
// if main tag slug name is not empty, this tag is synonymous with the main tag
|
||||
MainTagSlugName string `json:"main_tag_slug_name"`
|
||||
Recommend bool `json:"recommend"`
|
||||
Reserved bool `json:"reserved"`
|
||||
}
|
||||
|
||||
func (tr *GetTagResp) GetExcerpt() {
|
||||
excerpt := strings.TrimSpace(tr.ParsedText)
|
||||
idx := strings.Index(excerpt, "\n")
|
||||
if idx >= 0 {
|
||||
excerpt = excerpt[0:idx]
|
||||
}
|
||||
tr.Excerpt = excerpt
|
||||
}
|
||||
|
||||
// GetTagPageResp get tag response
|
||||
type GetTagPageResp struct {
|
||||
// tag_id
|
||||
TagID string `json:"tag_id"`
|
||||
// slug_name
|
||||
SlugName string `json:"slug_name"`
|
||||
// display_name
|
||||
DisplayName string `json:"display_name"`
|
||||
// excerpt
|
||||
Excerpt string `json:"excerpt"`
|
||||
// description
|
||||
Description string `json:"description"`
|
||||
// original text
|
||||
OriginalText string `json:"original_text"`
|
||||
// parsed_text
|
||||
ParsedText string `json:"parsed_text"`
|
||||
// follower amount
|
||||
FollowCount int `json:"follow_count"`
|
||||
// question amount
|
||||
QuestionCount int `json:"question_count"`
|
||||
// is follower
|
||||
IsFollower bool `json:"is_follower"`
|
||||
// created time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
// updated time
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
Recommend bool `json:"recommend"`
|
||||
Reserved bool `json:"reserved"`
|
||||
}
|
||||
|
||||
func (tr *GetTagPageResp) GetExcerpt() {
|
||||
excerpt := strings.TrimSpace(tr.ParsedText)
|
||||
idx := strings.Index(excerpt, "\n")
|
||||
if idx >= 0 {
|
||||
excerpt = excerpt[0:idx]
|
||||
}
|
||||
tr.Excerpt = excerpt
|
||||
}
|
||||
|
||||
type TagChange struct {
|
||||
ObjectID string `json:"object_id"` // object_id
|
||||
Tags []*TagItem `json:"tags"` // tags name
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type TagItem struct {
|
||||
// slug_name
|
||||
SlugName string `validate:"omitempty,gt=0,lte=35" json:"slug_name"`
|
||||
// display_name
|
||||
DisplayName string `validate:"omitempty,gt=0,lte=35" json:"display_name"`
|
||||
// original text
|
||||
OriginalText string `validate:"omitempty" json:"original_text"`
|
||||
// parsed text
|
||||
ParsedText string `json:"-"`
|
||||
}
|
||||
|
||||
// RemoveTagReq delete tag request
|
||||
type RemoveTagReq struct {
|
||||
// tag_id
|
||||
TagID string `validate:"required" json:"tag_id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// AddTagReq add tag request
|
||||
type AddTagReq struct {
|
||||
// slug_name
|
||||
SlugName string `validate:"required,gt=0,lte=35" json:"slug_name"`
|
||||
// display_name
|
||||
DisplayName string `validate:"required,gt=0,lte=35" json:"display_name"`
|
||||
// original text
|
||||
OriginalText string `validate:"required,gt=0,lte=65536" json:"original_text"`
|
||||
// parsed text
|
||||
ParsedText string `json:"-"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *AddTagReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.ParsedText = converter.Markdown2HTML(req.OriginalText)
|
||||
req.SlugName = strings.ToLower(req.SlugName)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// AddTagResp add tag response
|
||||
type AddTagResp struct {
|
||||
SlugName string `json:"slug_name"`
|
||||
}
|
||||
|
||||
// UpdateTagReq update tag request
|
||||
type UpdateTagReq struct {
|
||||
// tag_id
|
||||
TagID string `validate:"required" json:"tag_id"`
|
||||
// slug_name
|
||||
SlugName string `validate:"omitempty,gt=0,lte=35" json:"slug_name"`
|
||||
// display_name
|
||||
DisplayName string `validate:"omitempty,gt=0,lte=35" json:"display_name"`
|
||||
// original text
|
||||
OriginalText string `validate:"omitempty" json:"original_text"`
|
||||
// parsed text
|
||||
ParsedText string `json:"-"`
|
||||
// edit summary
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
NoNeedReview bool `json:"-"`
|
||||
}
|
||||
|
||||
func (r *UpdateTagReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
r.ParsedText = converter.Markdown2HTML(r.OriginalText)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// RecoverTagReq update tag request
|
||||
type RecoverTagReq struct {
|
||||
TagID string `validate:"required" json:"tag_id"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// UpdateTagResp update tag response
|
||||
type UpdateTagResp struct {
|
||||
WaitForReview bool `json:"wait_for_review"`
|
||||
}
|
||||
|
||||
// GetTagWithPageReq get tag list page request
|
||||
type GetTagWithPageReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// slug_name
|
||||
SlugName string `validate:"omitempty,gt=0,lte=35" form:"slug_name"`
|
||||
// display_name
|
||||
DisplayName string `validate:"omitempty,gt=0,lte=35" form:"display_name"`
|
||||
// query condition
|
||||
QueryCond string `validate:"omitempty,oneof=popular name newest" form:"query_cond"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// GetTagSynonymsReq get tag synonyms request
|
||||
type GetTagSynonymsReq struct {
|
||||
// tag_id
|
||||
TagID string `validate:"required" form:"tag_id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetTagSynonymsResp get tag synonyms response
|
||||
type GetTagSynonymsResp struct {
|
||||
// synonyms
|
||||
Synonyms []*TagSynonym `json:"synonyms"`
|
||||
// MemberActions
|
||||
MemberActions []*PermissionMemberAction `json:"member_actions"`
|
||||
}
|
||||
|
||||
type TagSynonym struct {
|
||||
// tag id
|
||||
TagID string `json:"tag_id"`
|
||||
// slug name
|
||||
SlugName string `json:"slug_name"`
|
||||
// display name
|
||||
DisplayName string `json:"display_name"`
|
||||
// if main tag slug name is not empty, this tag is synonymous with the main tag
|
||||
MainTagSlugName string `json:"main_tag_slug_name"`
|
||||
}
|
||||
|
||||
// UpdateTagSynonymReq update tag request
|
||||
type UpdateTagSynonymReq struct {
|
||||
// tag_id
|
||||
TagID string `validate:"required" json:"tag_id"`
|
||||
// synonym tag list
|
||||
SynonymTagList []*TagItem `validate:"required,dive" json:"synonym_tag_list"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *UpdateTagSynonymReq) Format() {
|
||||
for _, item := range req.SynonymTagList {
|
||||
item.SlugName = strings.ToLower(item.SlugName)
|
||||
}
|
||||
}
|
||||
|
||||
// GetFollowingTagsResp get following tags response
|
||||
type GetFollowingTagsResp struct {
|
||||
// tag id
|
||||
TagID string `json:"tag_id"`
|
||||
// slug name
|
||||
SlugName string `json:"slug_name"`
|
||||
// display name
|
||||
DisplayName string `json:"display_name"`
|
||||
// if main tag slug name is not empty, this tag is synonymous with the main tag
|
||||
MainTagSlugName string `json:"main_tag_slug_name"`
|
||||
Recommend bool `json:"recommend"`
|
||||
Reserved bool `json:"reserved"`
|
||||
}
|
||||
|
||||
// GetTagBasicResp get tag basic response
|
||||
type GetTagBasicResp struct {
|
||||
TagID string `json:"tag_id"`
|
||||
SlugName string `json:"slug_name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Recommend bool `json:"recommend"`
|
||||
Reserved bool `json:"reserved"`
|
||||
}
|
||||
|
||||
// MergeTagReq merge tag request
|
||||
type MergeTagReq struct {
|
||||
// source tag id
|
||||
SourceTagID string `validate:"required" json:"source_tag_id"`
|
||||
// target tag id
|
||||
TargetTagID string `validate:"required" json:"target_tag_id"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// MergeTagResp merge tag response
|
||||
type MergeTagResp struct {
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
type Paginator struct {
|
||||
Pages []int
|
||||
Totalpages int
|
||||
Prevpage int
|
||||
Nextpage int
|
||||
Currpage int
|
||||
}
|
||||
|
||||
type QAPageJsonLD struct {
|
||||
Context string `json:"@context"`
|
||||
Type string `json:"@type"`
|
||||
MainEntity struct {
|
||||
Type string `json:"@type"`
|
||||
Name string `json:"name"`
|
||||
Text string `json:"text"`
|
||||
AnswerCount int `json:"answerCount"`
|
||||
UpvoteCount int `json:"upvoteCount"`
|
||||
DateCreated time.Time `json:"dateCreated"`
|
||||
Author struct {
|
||||
URL string `json:"url"`
|
||||
Type string `json:"@type"`
|
||||
Name string `json:"name"`
|
||||
} `json:"author"`
|
||||
AcceptedAnswer *AcceptedAnswerItem `json:"acceptedAnswer,omitempty"`
|
||||
SuggestedAnswer []*SuggestedAnswerItem `json:"suggestedAnswer"`
|
||||
} `json:"mainEntity"`
|
||||
}
|
||||
|
||||
type AcceptedAnswerItem struct {
|
||||
Type string `json:"@type"`
|
||||
Text string `json:"text"`
|
||||
DateCreated time.Time `json:"dateCreated"`
|
||||
UpvoteCount int `json:"upvoteCount"`
|
||||
URL string `json:"url"`
|
||||
Author struct {
|
||||
URL string `json:"url"`
|
||||
Type string `json:"@type"`
|
||||
Name string `json:"name"`
|
||||
} `json:"author"`
|
||||
}
|
||||
|
||||
type SuggestedAnswerItem struct {
|
||||
Type string `json:"@type"`
|
||||
Text string `json:"text"`
|
||||
DateCreated time.Time `json:"dateCreated"`
|
||||
UpvoteCount int `json:"upvoteCount"`
|
||||
URL string `json:"url"`
|
||||
Author struct {
|
||||
URL string `json:"url"`
|
||||
Type string `json:"@type"`
|
||||
Name string `json:"name"`
|
||||
} `json:"author"`
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
var GetThemeOptions = []*ThemeOption{
|
||||
{
|
||||
Label: "Default",
|
||||
Value: "default",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
const (
|
||||
ExternalLoginOAuthStateLoginIntent = "login"
|
||||
ExternalLoginOAuthStateBindIntent = "bind"
|
||||
)
|
||||
|
||||
// UserExternalLoginResp user external login resp
|
||||
type UserExternalLoginResp struct {
|
||||
BindingKey string `json:"binding_key"`
|
||||
AccessToken string `json:"access_token"`
|
||||
// ErrMsg error message, if not empty, means login failed and this message should be displayed.
|
||||
ErrMsg string `json:"-"`
|
||||
ErrTitle string `json:"-"`
|
||||
}
|
||||
|
||||
// ExternalLoginBindingUserSendEmailReq external login binding user request
|
||||
type ExternalLoginBindingUserSendEmailReq struct {
|
||||
BindingKey string `validate:"required,gt=1,lte=100" json:"binding_key"`
|
||||
Email string `validate:"required,gt=1,lte=512,email" json:"email"`
|
||||
// If must is true, whatever email if exists, try to bind user.
|
||||
// If must is false, when email exist, will only be prompted with a warning.
|
||||
Must bool `json:"must"`
|
||||
}
|
||||
|
||||
// ExternalLoginBindingUserSendEmailResp external login binding user response
|
||||
type ExternalLoginBindingUserSendEmailResp struct {
|
||||
EmailExistAndMustBeConfirmed bool `json:"email_exist_and_must_be_confirmed"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
// ExternalLoginBindingUserReq external login binding user request
|
||||
type ExternalLoginBindingUserReq struct {
|
||||
Code string `validate:"required,gt=0,lte=500" json:"code"`
|
||||
Content string `json:"-"`
|
||||
}
|
||||
|
||||
// ExternalLoginBindingUserResp external login binding user response
|
||||
type ExternalLoginBindingUserResp struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
// ExternalLoginUserInfoCache external login user info
|
||||
type ExternalLoginUserInfoCache struct {
|
||||
// Third party identification
|
||||
// e.g. facebook, twitter, instagram
|
||||
Provider string
|
||||
// required. The unique user ID provided by the third-party login
|
||||
ExternalID string
|
||||
// optional. This name is used preferentially during registration
|
||||
DisplayName string
|
||||
// optional. This username is used preferentially during registration
|
||||
Username string
|
||||
// optional. If email exist will bind the existing user
|
||||
Email string
|
||||
// optional. The avatar URL provided by the third-party login platform
|
||||
Avatar string
|
||||
// optional. The original user information provided by the third-party login platform
|
||||
MetaInfo string
|
||||
// optional. The bio provided by the third-party login platform
|
||||
Bio string
|
||||
}
|
||||
|
||||
// ExternalLoginOAuthState stores the local OAuth request state.
|
||||
type ExternalLoginOAuthState struct {
|
||||
Provider string `json:"provider"`
|
||||
Intent string `json:"intent"`
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
}
|
||||
|
||||
// ExternalLoginUnbindingReq external login unbinding user
|
||||
type ExternalLoginUnbindingReq struct {
|
||||
ExternalID string `validate:"required,gt=0,lte=128" json:"external_id"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// UserCenterUserSettingsResp user center user info response
|
||||
type UserCenterUserSettingsResp struct {
|
||||
ProfileSettingAgent UserSettingAgent `json:"profile_setting_agent"`
|
||||
AccountSettingAgent UserSettingAgent `json:"account_setting_agent"`
|
||||
}
|
||||
|
||||
type UserCenterAdminFunctionAgentResp struct {
|
||||
AllowCreateUser bool `json:"allow_create_user"`
|
||||
AllowUpdateUserStatus bool `json:"allow_update_user_status"`
|
||||
AllowUpdateUserPassword bool `json:"allow_update_user_password"`
|
||||
AllowUpdateUserRole bool `json:"allow_update_user_role"`
|
||||
}
|
||||
|
||||
type UserSettingAgent struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
RedirectURL string `json:"redirect_url"`
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/internal/entity"
|
||||
)
|
||||
|
||||
type NotificationChannelConfig struct {
|
||||
Key constant.NotificationChannelKey `json:"key"`
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
type NotificationChannels []*NotificationChannelConfig
|
||||
|
||||
func NewNotificationChannelsFormJson(jsonStr string) NotificationChannels {
|
||||
var list NotificationChannels
|
||||
_ = json.Unmarshal([]byte(jsonStr), &list)
|
||||
return list
|
||||
}
|
||||
|
||||
func NewNotificationChannelConfigFormJson(jsonStr string) NotificationChannelConfig {
|
||||
var list NotificationChannels
|
||||
_ = json.Unmarshal([]byte(jsonStr), &list)
|
||||
if len(list) > 0 {
|
||||
return *list[0]
|
||||
}
|
||||
return NotificationChannelConfig{}
|
||||
}
|
||||
|
||||
func (n *NotificationChannels) ToJsonString() string {
|
||||
data, _ := json.Marshal(n)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
type NotificationConfig struct {
|
||||
Inbox NotificationChannelConfig `json:"inbox"`
|
||||
AllNewQuestion NotificationChannelConfig `json:"all_new_question"`
|
||||
AllNewQuestionForFollowingTags NotificationChannelConfig `json:"all_new_question_for_following_tags"`
|
||||
}
|
||||
|
||||
func NewNotificationConfig(configs []*entity.UserNotificationConfig) NotificationConfig {
|
||||
nc := NotificationConfig{}
|
||||
for _, item := range configs {
|
||||
switch item.Source {
|
||||
case string(constant.InboxSource):
|
||||
nc.Inbox = NewNotificationChannelConfigFormJson(item.Channels)
|
||||
case string(constant.AllNewQuestionSource):
|
||||
nc.AllNewQuestion = NewNotificationChannelConfigFormJson(item.Channels)
|
||||
case string(constant.AllNewQuestionForFollowingTagsSource):
|
||||
nc.AllNewQuestionForFollowingTags = NewNotificationChannelConfigFormJson(item.Channels)
|
||||
}
|
||||
}
|
||||
return nc
|
||||
}
|
||||
|
||||
func (n *NotificationConfig) Format() {
|
||||
if n.Inbox.Key == "" {
|
||||
n.Inbox.Key = constant.EmailChannel
|
||||
n.Inbox.Enable = false
|
||||
}
|
||||
if n.AllNewQuestion.Key == "" {
|
||||
n.AllNewQuestion.Key = constant.EmailChannel
|
||||
n.AllNewQuestion.Enable = false
|
||||
}
|
||||
if n.AllNewQuestionForFollowingTags.Key == "" {
|
||||
n.AllNewQuestionForFollowingTags.Key = constant.EmailChannel
|
||||
n.AllNewQuestionForFollowingTags.Enable = false
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUserNotificationConfigReq update user notification config request
|
||||
type UpdateUserNotificationConfigReq struct {
|
||||
NotificationConfig
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
// GetUserNotificationConfigResp get user notification config response
|
||||
type GetUserNotificationConfigResp struct {
|
||||
NotificationConfig
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/apache/answer/internal/base/handler"
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/apache/answer/internal/base/translator"
|
||||
"github.com/apache/answer/pkg/day"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/internal/base/validator"
|
||||
"github.com/apache/answer/internal/entity"
|
||||
"github.com/apache/answer/pkg/checker"
|
||||
"github.com/apache/answer/pkg/converter"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
// UserVerifyEmailReq user verify email request
|
||||
type UserVerifyEmailReq struct {
|
||||
// code
|
||||
Code string `validate:"required,gt=0,lte=500" form:"code"`
|
||||
// content
|
||||
Content string `json:"-"`
|
||||
}
|
||||
|
||||
// UserLoginResp get user response
|
||||
type UserLoginResp struct {
|
||||
// user id
|
||||
ID string `json:"id"`
|
||||
// create time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
// last login date
|
||||
LastLoginDate int64 `json:"last_login_date"`
|
||||
// username
|
||||
Username string `json:"username"`
|
||||
// email
|
||||
EMail string `json:"e_mail"`
|
||||
// mail status(1 pass 2 to be verified)
|
||||
MailStatus int `json:"mail_status"`
|
||||
// notice status(1 on 2off)
|
||||
NoticeStatus int `json:"notice_status"`
|
||||
// follow count
|
||||
FollowCount int `json:"follow_count"`
|
||||
// answer count
|
||||
AnswerCount int `json:"answer_count"`
|
||||
// question count
|
||||
QuestionCount int `json:"question_count"`
|
||||
// rank
|
||||
Rank int `json:"rank"`
|
||||
// authority group
|
||||
AuthorityGroup int `json:"authority_group"`
|
||||
// display name
|
||||
DisplayName string `json:"display_name"`
|
||||
// avatar
|
||||
Avatar string `json:"avatar"`
|
||||
// mobile
|
||||
Mobile string `json:"mobile"`
|
||||
// bio markdown
|
||||
Bio string `json:"bio"`
|
||||
// bio html
|
||||
BioHTML string `json:"bio_html"`
|
||||
// website
|
||||
Website string `json:"website"`
|
||||
// location
|
||||
Location string `json:"location"`
|
||||
// language
|
||||
Language string `json:"language"`
|
||||
// Color scheme
|
||||
ColorScheme string `json:"color_scheme"`
|
||||
// access token
|
||||
AccessToken string `json:"access_token"`
|
||||
// role id
|
||||
RoleID int `json:"role_id"`
|
||||
// user status
|
||||
Status string `json:"status"`
|
||||
// user have password
|
||||
HavePassword bool `json:"have_password"`
|
||||
// visit token
|
||||
VisitToken string `json:"visit_token"`
|
||||
// suspended until timestamp
|
||||
SuspendedUntil int64 `json:"suspended_until"`
|
||||
}
|
||||
|
||||
func (r *UserLoginResp) ConvertFromUserEntity(userInfo *entity.User) {
|
||||
_ = copier.Copy(r, userInfo)
|
||||
r.CreatedAt = userInfo.CreatedAt.Unix()
|
||||
r.LastLoginDate = userInfo.LastLoginDate.Unix()
|
||||
r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus)
|
||||
r.HavePassword = len(userInfo.Pass) > 0
|
||||
if !userInfo.SuspendedUntil.IsZero() {
|
||||
r.SuspendedUntil = userInfo.SuspendedUntil.Unix()
|
||||
}
|
||||
}
|
||||
|
||||
type GetCurrentLoginUserInfoResp struct {
|
||||
*UserLoginResp
|
||||
Avatar *AvatarInfo `json:"avatar"`
|
||||
}
|
||||
|
||||
func (r *GetCurrentLoginUserInfoResp) ConvertFromUserEntity(userInfo *entity.User) {
|
||||
_ = copier.Copy(r, userInfo)
|
||||
r.CreatedAt = userInfo.CreatedAt.Unix()
|
||||
r.LastLoginDate = userInfo.LastLoginDate.Unix()
|
||||
r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus)
|
||||
if len(r.ColorScheme) == 0 {
|
||||
r.ColorScheme = constant.ColorSchemeDefault
|
||||
}
|
||||
if !userInfo.SuspendedUntil.IsZero() {
|
||||
r.SuspendedUntil = userInfo.SuspendedUntil.Unix()
|
||||
}
|
||||
}
|
||||
|
||||
// GetOtherUserInfoByUsernameResp get user response
|
||||
type GetOtherUserInfoByUsernameResp struct {
|
||||
// user id
|
||||
ID string `json:"id"`
|
||||
// create time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
// last login date
|
||||
LastLoginDate int64 `json:"last_login_date"`
|
||||
// username
|
||||
Username string `json:"username"`
|
||||
// email
|
||||
// follow count
|
||||
FollowCount int `json:"follow_count"`
|
||||
// answer count
|
||||
AnswerCount int `json:"answer_count"`
|
||||
// question count
|
||||
QuestionCount int `json:"question_count"`
|
||||
// rank
|
||||
Rank int `json:"rank"`
|
||||
// display name
|
||||
DisplayName string `json:"display_name"`
|
||||
// avatar
|
||||
Avatar string `json:"avatar"`
|
||||
// mobile
|
||||
Mobile string `json:"mobile"`
|
||||
// bio markdown
|
||||
Bio string `json:"bio"`
|
||||
// bio html
|
||||
BioHTML string `json:"bio_html"`
|
||||
// website
|
||||
Website string `json:"website"`
|
||||
// location
|
||||
Location string `json:"location"`
|
||||
Status string `json:"status"`
|
||||
StatusMsg string `json:"status_msg,omitempty"`
|
||||
// suspended until timestamp
|
||||
SuspendedUntil int64 `json:"suspended_until"`
|
||||
}
|
||||
|
||||
func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntity(userInfo *entity.User) {
|
||||
_ = copier.Copy(r, userInfo)
|
||||
r.CreatedAt = userInfo.CreatedAt.Unix()
|
||||
r.LastLoginDate = userInfo.LastLoginDate.Unix()
|
||||
r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus)
|
||||
if !userInfo.SuspendedUntil.IsZero() {
|
||||
r.SuspendedUntil = userInfo.SuspendedUntil.Unix()
|
||||
}
|
||||
r.StatusMsg = ""
|
||||
}
|
||||
|
||||
func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntityWithLang(ctx context.Context, userInfo *entity.User) {
|
||||
_ = copier.Copy(r, userInfo)
|
||||
r.CreatedAt = userInfo.CreatedAt.Unix()
|
||||
r.LastLoginDate = userInfo.LastLoginDate.Unix()
|
||||
r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus)
|
||||
|
||||
lang := handler.GetLangByCtx(ctx)
|
||||
if userInfo.MailStatus == entity.EmailStatusToBeVerified {
|
||||
r.StatusMsg = translator.Tr(lang, reason.UserStatusInactive)
|
||||
}
|
||||
switch userInfo.Status {
|
||||
case entity.UserStatusSuspended:
|
||||
if userInfo.SuspendedUntil.IsZero() || userInfo.SuspendedUntil.Year() >= 2099 {
|
||||
r.StatusMsg = translator.Tr(lang, reason.UserStatusSuspendedForever)
|
||||
} else {
|
||||
r.SuspendedUntil = userInfo.SuspendedUntil.Unix()
|
||||
trans := translator.GlobalTrans.Tr(lang, "ui.dates.long_date_with_time")
|
||||
suspendedUntilFormatted := day.Format(userInfo.SuspendedUntil.Unix(), trans, "UTC")
|
||||
r.StatusMsg = translator.TrWithData(lang, reason.UserStatusSuspendedUntil, map[string]any{
|
||||
"SuspendedUntil": suspendedUntilFormatted,
|
||||
})
|
||||
}
|
||||
case entity.UserStatusDeleted:
|
||||
r.StatusMsg = translator.Tr(lang, reason.UserStatusDeleted)
|
||||
}
|
||||
}
|
||||
|
||||
// UserEmailLoginReq user email login request
|
||||
type UserEmailLoginReq struct {
|
||||
Email string `validate:"required,email,gt=0,lte=500" json:"e_mail"`
|
||||
Pass string `validate:"required,gte=8,lte=32" json:"pass"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
// UserRegisterReq user register request
|
||||
type UserRegisterReq struct {
|
||||
Name string `validate:"required,gte=2,lte=30" json:"name"`
|
||||
Email string `validate:"required,email,gt=0,lte=500" json:"e_mail" `
|
||||
Pass string `validate:"required,gte=8,lte=32" json:"pass"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
IP string `json:"-" `
|
||||
RequireEmailVerification bool `json:"-"`
|
||||
}
|
||||
|
||||
func (u *UserRegisterReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
if err = checker.CheckPassword(u.Pass); err != nil {
|
||||
errFields = append(errFields, &validator.FormErrorField{
|
||||
ErrorField: "pass",
|
||||
ErrorMsg: err.Error(),
|
||||
})
|
||||
return errFields, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type UserModifyPasswordReq struct {
|
||||
OldPass string `validate:"omitempty,gte=8,lte=32" json:"old_pass"`
|
||||
Pass string `validate:"required,gte=8,lte=32" json:"pass"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
UserID string `json:"-"`
|
||||
AccessToken string `json:"-"`
|
||||
}
|
||||
|
||||
func (u *UserModifyPasswordReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
if err = checker.CheckPassword(u.Pass); err != nil {
|
||||
errFields = append(errFields, &validator.FormErrorField{
|
||||
ErrorField: "pass",
|
||||
ErrorMsg: err.Error(),
|
||||
})
|
||||
return errFields, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type UpdateInfoRequest struct {
|
||||
DisplayName string `validate:"omitempty,gte=2,lte=30" json:"display_name"`
|
||||
Username string `validate:"omitempty,gte=2,lte=30" json:"username"`
|
||||
Avatar AvatarInfo `json:"avatar"`
|
||||
Bio string `validate:"omitempty,gt=0,lte=4096" json:"bio"`
|
||||
BioHTML string `json:"-"`
|
||||
Website string `validate:"omitempty,gt=0,lte=500" json:"website"`
|
||||
Location string `validate:"omitempty,gt=0,lte=100" json:"location"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
type AvatarInfo struct {
|
||||
Type string `validate:"omitempty,gt=0,lte=100" json:"type"`
|
||||
Gravatar string `validate:"omitempty,gt=0,lte=200" json:"gravatar"`
|
||||
Custom string `validate:"omitempty,gt=0,lte=200" json:"custom"`
|
||||
}
|
||||
|
||||
func (a *AvatarInfo) ToJsonString() string {
|
||||
data, _ := json.Marshal(a)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (a *AvatarInfo) GetURL() string {
|
||||
switch a.Type {
|
||||
case constant.AvatarTypeGravatar:
|
||||
return a.Gravatar
|
||||
case constant.AvatarTypeCustom:
|
||||
return a.Custom
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func CustomAvatar(url string) *AvatarInfo {
|
||||
return &AvatarInfo{
|
||||
Type: constant.AvatarTypeCustom,
|
||||
Custom: url,
|
||||
}
|
||||
}
|
||||
|
||||
func (req *UpdateInfoRequest) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
req.BioHTML = converter.Markdown2BasicHTML(req.Bio)
|
||||
if len(req.Website) > 0 && !checker.IsURL(req.Website) {
|
||||
return append(errFields, &validator.FormErrorField{
|
||||
ErrorField: "website",
|
||||
ErrorMsg: reason.InvalidURLError,
|
||||
}), errors.BadRequest(reason.InvalidURLError)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// UpdateUserInterfaceRequest update user interface request
|
||||
type UpdateUserInterfaceRequest struct {
|
||||
// language
|
||||
Language string `validate:"required,gt=1,lte=100" json:"language"`
|
||||
// Color scheme
|
||||
ColorScheme string `validate:"required,gt=1,lte=100" json:"color_scheme"`
|
||||
// user id
|
||||
UserId string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *UpdateUserInterfaceRequest) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
if !translator.CheckLanguageIsValid(req.Language) {
|
||||
return nil, errors.BadRequest(reason.LangNotFound)
|
||||
}
|
||||
if req.ColorScheme != constant.ColorSchemeDefault &&
|
||||
req.ColorScheme != constant.ColorSchemeLight &&
|
||||
req.ColorScheme != constant.ColorSchemeDark &&
|
||||
req.ColorScheme != constant.ColorSchemeSystem {
|
||||
req.ColorScheme = constant.ColorSchemeDefault
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type UserRetrievePassWordRequest struct {
|
||||
Email string `validate:"required,email,gt=0,lte=500" json:"e_mail"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
type UserRePassWordRequest struct {
|
||||
Code string `validate:"required,gt=0,lte=100" json:"code"`
|
||||
Pass string `validate:"required,gt=0,lte=32" json:"pass"`
|
||||
Content string `json:"-"`
|
||||
}
|
||||
|
||||
func (u *UserRePassWordRequest) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
if err = checker.CheckPassword(u.Pass); err != nil {
|
||||
errFields = append(errFields, &validator.FormErrorField{
|
||||
ErrorField: "pass",
|
||||
ErrorMsg: err.Error(),
|
||||
})
|
||||
return errFields, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type ActionRecordReq struct {
|
||||
Action string `validate:"required,oneof=email password edit_userinfo question answer comment edit invitation_answer search report delete vote" form:"action"`
|
||||
IP string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type ActionRecordResp struct {
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaImg string `json:"captcha_img"`
|
||||
Verify bool `json:"verify"`
|
||||
}
|
||||
|
||||
type UserBasicInfo struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Rank int `json:"rank"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Website string `json:"website"`
|
||||
Location string `json:"location"`
|
||||
Language string `json:"language"`
|
||||
Status string `json:"status"`
|
||||
SuspendedUntil int64 `json:"suspended_until"`
|
||||
}
|
||||
|
||||
type GetOtherUserInfoByUsernameReq struct {
|
||||
Username string `validate:"required,gt=0,lte=500" form:"username"`
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
}
|
||||
|
||||
type GetOtherUserInfoResp struct {
|
||||
Info *GetOtherUserInfoByUsernameResp `json:"info"`
|
||||
}
|
||||
|
||||
type UserChangeEmailSendCodeReq struct {
|
||||
UserVerifyEmailSendReq
|
||||
Email string `validate:"required,email,gt=0,lte=500" json:"e_mail"`
|
||||
Pass string `validate:"omitempty,gte=8,lte=32" json:"pass"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type UserChangeEmailVerifyReq struct {
|
||||
Code string `validate:"required,gt=0,lte=500" json:"code"`
|
||||
Content string `json:"-"`
|
||||
}
|
||||
|
||||
type UserVerifyEmailSendReq struct {
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
}
|
||||
|
||||
// UserRankingResp user ranking response
|
||||
type UserRankingResp struct {
|
||||
UsersWithTheMostReputation []*UserRankingSimpleInfo `json:"users_with_the_most_reputation"`
|
||||
UsersWithTheMostVote []*UserRankingSimpleInfo `json:"users_with_the_most_vote"`
|
||||
Staffs []*UserRankingSimpleInfo `json:"staffs"`
|
||||
}
|
||||
|
||||
// UserRankingSimpleInfo user ranking simple info
|
||||
type UserRankingSimpleInfo struct {
|
||||
// username
|
||||
Username string `json:"username"`
|
||||
// rank
|
||||
Rank int `json:"rank"`
|
||||
// vote
|
||||
VoteCount int `json:"vote_count"`
|
||||
// display name
|
||||
DisplayName string `json:"display_name"`
|
||||
// avatar
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
// UserUnsubscribeNotificationReq user unsubscribe email notification request
|
||||
type UserUnsubscribeNotificationReq struct {
|
||||
Code string `validate:"required,gt=0,lte=500" json:"code"`
|
||||
Content string `json:"-"`
|
||||
}
|
||||
|
||||
// GetUserStaffReq get user staff request
|
||||
type GetUserStaffReq struct {
|
||||
Username string `validate:"omitempty,gt=0,lte=500" form:"username"`
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
}
|
||||
|
||||
// GetUserStaffResp get user staff response
|
||||
type GetUserStaffResp struct {
|
||||
// username
|
||||
Username string `json:"username"`
|
||||
// display name
|
||||
DisplayName string `json:"display_name"`
|
||||
// avatar
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package schema
|
||||
|
||||
type VoteReq struct {
|
||||
ObjectID string `validate:"required" json:"object_id"`
|
||||
IsCancel bool `validate:"omitempty" json:"is_cancel"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
CaptchaCode string `json:"captcha_code"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type VoteResp struct {
|
||||
UpVotes int64 `json:"up_votes"`
|
||||
DownVotes int64 `json:"down_votes"`
|
||||
Votes int64 `json:"votes"`
|
||||
VoteStatus string `json:"vote_status"`
|
||||
}
|
||||
|
||||
// VoteOperationInfo vote operation info
|
||||
type VoteOperationInfo struct {
|
||||
// operation object id
|
||||
ObjectID string
|
||||
// question answer comment
|
||||
ObjectType string
|
||||
// object owner user id
|
||||
ObjectCreatorUserID string
|
||||
// operation user id
|
||||
OperatingUserID string
|
||||
// vote up
|
||||
VoteUp bool
|
||||
// vote down
|
||||
VoteDown bool
|
||||
// vote activity info
|
||||
Activities []*VoteActivity
|
||||
}
|
||||
|
||||
// VoteActivity vote activity
|
||||
type VoteActivity struct {
|
||||
ActivityType int
|
||||
ActivityUserID string
|
||||
TriggerUserID string
|
||||
Rank int
|
||||
}
|
||||
|
||||
func (v *VoteActivity) HasRank() int {
|
||||
if v.Rank != 0 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GetVoteWithPageReq struct {
|
||||
// page
|
||||
Page int `validate:"omitempty,min=1" form:"page"`
|
||||
// page size
|
||||
PageSize int `validate:"omitempty,min=1" form:"page_size"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type GetVoteWithPageResp struct {
|
||||
// create time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
// object id
|
||||
ObjectID string `json:"object_id"`
|
||||
// question id
|
||||
QuestionID string `json:"question_id"`
|
||||
// answer id
|
||||
AnswerID string `json:"answer_id"`
|
||||
// object type
|
||||
ObjectType string `json:"object_type" enums:"question,answer,tag,comment"`
|
||||
// title
|
||||
Title string `json:"title"`
|
||||
// url title
|
||||
UrlTitle string `json:"url_title"`
|
||||
// content
|
||||
Content string `json:"content"`
|
||||
// vote type
|
||||
VoteType string `json:"vote_type"`
|
||||
}
|
||||
Reference in New Issue
Block a user