Merge remote-tracking branch 'origin/feat/0.7.0/user-manage' into test

This commit is contained in:
LinkinStar 2022-12-12 13:16:30 +08:00
commit 1d3c6e531c
31 changed files with 1858 additions and 216 deletions

View File

@ -116,14 +116,14 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
roleRepo := role.NewRoleRepo(dataData)
roleService := role2.NewRoleService(roleRepo)
userRoleRelService := role2.NewUserRoleRelService(userRoleRelRepo, roleService)
userService := service.NewUserService(userRepo, userActiveActivityRepo, emailService, authService, serviceConf, siteInfoCommonService, userRoleRelService)
userCommon := usercommon.NewUserCommon(userRepo)
userService := service.NewUserService(userRepo, userActiveActivityRepo, emailService, authService, serviceConf, siteInfoCommonService, userRoleRelService, userCommon)
captchaRepo := captcha.NewCaptchaRepo(dataData)
captchaService := action.NewCaptchaService(captchaRepo)
uploaderService := uploader.NewUploaderService(serviceConf, siteInfoCommonService)
userController := controller.NewUserController(authService, userService, captchaService, emailService, uploaderService)
userController := controller.NewUserController(authService, userService, captchaService, emailService, uploaderService, siteInfoCommonService)
commentRepo := comment.NewCommentRepo(dataData, uniqueIDRepo)
commentCommonRepo := comment.NewCommentCommonRepo(dataData, uniqueIDRepo)
userCommon := usercommon.NewUserCommon(userRepo)
answerRepo := answer.NewAnswerRepo(dataData, uniqueIDRepo, userRankRepo, activityRepo)
questionRepo := question.NewQuestionRepo(dataData, uniqueIDRepo)
tagCommonRepo := tag_common.NewTagCommonRepo(dataData, uniqueIDRepo)
@ -180,13 +180,13 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
reportBackyardService := report_backyard.NewReportBackyardService(reportRepo, userCommon, commonRepo, answerRepo, questionRepo, commentCommonRepo, reportHandle, configRepo)
controller_backyardReportController := controller_backyard.NewReportController(reportBackyardService)
userBackyardRepo := user.NewUserBackyardRepo(dataData, authRepo)
userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo, userRoleRelService, authService)
userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo, userRoleRelService, authService, userCommon)
userBackyardController := controller_backyard.NewUserBackyardController(userBackyardService)
reasonRepo := reason.NewReasonRepo(configRepo)
reasonService := reason2.NewReasonService(reasonRepo)
reasonController := controller.NewReasonController(reasonService)
themeController := controller_backyard.NewThemeController()
siteInfoService := siteinfo.NewSiteInfoService(siteInfoRepo, emailService, tagCommonService)
siteInfoService := siteinfo.NewSiteInfoService(siteInfoRepo, siteInfoCommonService, emailService, tagCommonService)
siteInfoController := controller_backyard.NewSiteInfoController(siteInfoService)
siteinfoController := controller.NewSiteinfoController(siteInfoCommonService)
notificationRepo := notification.NewNotificationRepo(dataData)
@ -204,7 +204,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, controller_backyardReportController, userBackyardController, reasonController, themeController, siteInfoController, siteinfoController, notificationController, dashboardController, uploadController, activityController, roleController)
swaggerRouter := router.NewSwaggerRouter(swaggerConf)
uiRouter := router.NewUIRouter(siteinfoController)
authUserMiddleware := middleware.NewAuthUserMiddleware(authService)
authUserMiddleware := middleware.NewAuthUserMiddleware(authService, siteInfoCommonService)
avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService)
templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService, answerService, commentService)
templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService)

View File

@ -609,6 +609,77 @@ const docTemplate = `{
}
}
},
"/answer/admin/api/siteinfo/custom-css-html": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "get site info custom html css config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "get site info custom html css config",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/handler.RespBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.SiteCustomCssHTMLResp"
}
}
}
]
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update site custom css html config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "update site custom css html config",
"parameters": [
{
"description": "login info",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.SiteCustomCssHTMLReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/siteinfo/general": {
"get": {
"security": [
@ -822,6 +893,77 @@ const docTemplate = `{
}
}
},
"/answer/admin/api/siteinfo/login": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "get site info login config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "get site info login config",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/handler.RespBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.SiteLoginResp"
}
}
}
]
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update site login",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "update site login",
"parameters": [
{
"description": "login info",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.SiteLoginReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/siteinfo/seo": {
"get": {
"security": [
@ -893,6 +1035,77 @@ const docTemplate = `{
}
}
},
"/answer/admin/api/siteinfo/theme": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "get site info theme config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "get site info theme config",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/handler.RespBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.SiteThemeResp"
}
}
}
]
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update site custom css html config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "update site custom css html config",
"parameters": [
{
"description": "login info",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.SiteThemeReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/siteinfo/write": {
"get": {
"security": [
@ -989,6 +1202,84 @@ const docTemplate = `{
}
}
},
"/answer/admin/api/user": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "add user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "add user",
"parameters": [
{
"description": "user",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.AddUserReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/user/password": {
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update user password",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "update user password",
"parameters": [
{
"description": "user",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.UpdateUserPasswordReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/user/role": {
"put": {
"security": [
@ -3456,7 +3747,7 @@ const docTemplate = `{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.SiteGeneralResp"
"$ref": "#/definitions/schema.SiteInfoResp"
}
}
}
@ -5059,6 +5350,29 @@ const docTemplate = `{
}
}
},
"schema.AddUserReq": {
"type": "object",
"required": [
"display_name",
"email",
"password"
],
"properties": {
"display_name": {
"type": "string",
"maxLength": 30
},
"email": {
"type": "string",
"maxLength": 500
},
"password": {
"type": "string",
"maxLength": 32,
"minLength": 8
}
}
},
"schema.AdminSetAnswerStatusRequest": {
"type": "object",
"properties": {
@ -6470,6 +6784,48 @@ const docTemplate = `{
}
}
},
"schema.SiteCustomCssHTMLReq": {
"type": "object",
"properties": {
"custom_css": {
"type": "string",
"maxLength": 65536
},
"custom_footer": {
"type": "string",
"maxLength": 65536
},
"custom_head": {
"type": "string",
"maxLength": 65536
},
"custom_header": {
"type": "string",
"maxLength": 65536
}
}
},
"schema.SiteCustomCssHTMLResp": {
"type": "object",
"properties": {
"custom_css": {
"type": "string",
"maxLength": 65536
},
"custom_footer": {
"type": "string",
"maxLength": 65536
},
"custom_head": {
"type": "string",
"maxLength": 65536
},
"custom_header": {
"type": "string",
"maxLength": 65536
}
}
},
"schema.SiteGeneralReq": {
"type": "object",
"required": [
@ -6542,6 +6898,26 @@ const docTemplate = `{
}
}
},
"schema.SiteInfoResp": {
"type": "object",
"properties": {
"branding": {
"$ref": "#/definitions/schema.SiteBrandingResp"
},
"general": {
"$ref": "#/definitions/schema.SiteGeneralResp"
},
"interface": {
"$ref": "#/definitions/schema.SiteInterfaceResp"
},
"login": {
"$ref": "#/definitions/schema.SiteLoginResp"
},
"theme": {
"$ref": "#/definitions/schema.SiteThemeResp"
}
}
},
"schema.SiteInterfaceReq": {
"type": "object",
"required": [
@ -6620,6 +6996,28 @@ const docTemplate = `{
}
}
},
"schema.SiteLoginReq": {
"type": "object",
"properties": {
"allow_new_registrations": {
"type": "boolean"
},
"login_required": {
"type": "boolean"
}
}
},
"schema.SiteLoginResp": {
"type": "object",
"properties": {
"allow_new_registrations": {
"type": "boolean"
},
"login_required": {
"type": "boolean"
}
}
},
"schema.SiteSeoReq": {
"type": "object",
"required": [
@ -6642,6 +7040,38 @@ const docTemplate = `{
}
}
},
"schema.SiteThemeReq": {
"type": "object",
"required": [
"theme"
],
"properties": {
"theme": {
"type": "string",
"maxLength": 255
},
"theme_config": {
"type": "object",
"additionalProperties": true
}
}
},
"schema.SiteThemeResp": {
"type": "object",
"required": [
"theme"
],
"properties": {
"theme": {
"type": "string",
"maxLength": 255
},
"theme_config": {
"type": "object",
"additionalProperties": true
}
}
},
"schema.SiteWriteReq": {
"type": "object",
"properties": {
@ -6954,6 +7384,23 @@ const docTemplate = `{
}
}
},
"schema.UpdateUserPasswordReq": {
"type": "object",
"required": [
"password",
"user_id"
],
"properties": {
"password": {
"type": "string",
"maxLength": 32,
"minLength": 8
},
"user_id": {
"type": "string"
}
}
},
"schema.UpdateUserRoleReq": {
"type": "object",
"required": [

View File

@ -597,6 +597,77 @@
}
}
},
"/answer/admin/api/siteinfo/custom-css-html": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "get site info custom html css config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "get site info custom html css config",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/handler.RespBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.SiteCustomCssHTMLResp"
}
}
}
]
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update site custom css html config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "update site custom css html config",
"parameters": [
{
"description": "login info",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.SiteCustomCssHTMLReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/siteinfo/general": {
"get": {
"security": [
@ -810,6 +881,77 @@
}
}
},
"/answer/admin/api/siteinfo/login": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "get site info login config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "get site info login config",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/handler.RespBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.SiteLoginResp"
}
}
}
]
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update site login",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "update site login",
"parameters": [
{
"description": "login info",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.SiteLoginReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/siteinfo/seo": {
"get": {
"security": [
@ -881,6 +1023,77 @@
}
}
},
"/answer/admin/api/siteinfo/theme": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "get site info theme config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "get site info theme config",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/handler.RespBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.SiteThemeResp"
}
}
}
]
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update site custom css html config",
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "update site custom css html config",
"parameters": [
{
"description": "login info",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.SiteThemeReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/siteinfo/write": {
"get": {
"security": [
@ -977,6 +1190,84 @@
}
}
},
"/answer/admin/api/user": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "add user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "add user",
"parameters": [
{
"description": "user",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.AddUserReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/user/password": {
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update user password",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "update user password",
"parameters": [
{
"description": "user",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.UpdateUserPasswordReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/user/role": {
"put": {
"security": [
@ -3444,7 +3735,7 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.SiteGeneralResp"
"$ref": "#/definitions/schema.SiteInfoResp"
}
}
}
@ -5047,6 +5338,29 @@
}
}
},
"schema.AddUserReq": {
"type": "object",
"required": [
"display_name",
"email",
"password"
],
"properties": {
"display_name": {
"type": "string",
"maxLength": 30
},
"email": {
"type": "string",
"maxLength": 500
},
"password": {
"type": "string",
"maxLength": 32,
"minLength": 8
}
}
},
"schema.AdminSetAnswerStatusRequest": {
"type": "object",
"properties": {
@ -6458,6 +6772,48 @@
}
}
},
"schema.SiteCustomCssHTMLReq": {
"type": "object",
"properties": {
"custom_css": {
"type": "string",
"maxLength": 65536
},
"custom_footer": {
"type": "string",
"maxLength": 65536
},
"custom_head": {
"type": "string",
"maxLength": 65536
},
"custom_header": {
"type": "string",
"maxLength": 65536
}
}
},
"schema.SiteCustomCssHTMLResp": {
"type": "object",
"properties": {
"custom_css": {
"type": "string",
"maxLength": 65536
},
"custom_footer": {
"type": "string",
"maxLength": 65536
},
"custom_head": {
"type": "string",
"maxLength": 65536
},
"custom_header": {
"type": "string",
"maxLength": 65536
}
}
},
"schema.SiteGeneralReq": {
"type": "object",
"required": [
@ -6530,6 +6886,26 @@
}
}
},
"schema.SiteInfoResp": {
"type": "object",
"properties": {
"branding": {
"$ref": "#/definitions/schema.SiteBrandingResp"
},
"general": {
"$ref": "#/definitions/schema.SiteGeneralResp"
},
"interface": {
"$ref": "#/definitions/schema.SiteInterfaceResp"
},
"login": {
"$ref": "#/definitions/schema.SiteLoginResp"
},
"theme": {
"$ref": "#/definitions/schema.SiteThemeResp"
}
}
},
"schema.SiteInterfaceReq": {
"type": "object",
"required": [
@ -6608,6 +6984,28 @@
}
}
},
"schema.SiteLoginReq": {
"type": "object",
"properties": {
"allow_new_registrations": {
"type": "boolean"
},
"login_required": {
"type": "boolean"
}
}
},
"schema.SiteLoginResp": {
"type": "object",
"properties": {
"allow_new_registrations": {
"type": "boolean"
},
"login_required": {
"type": "boolean"
}
}
},
"schema.SiteSeoReq": {
"type": "object",
"required": [
@ -6630,6 +7028,38 @@
}
}
},
"schema.SiteThemeReq": {
"type": "object",
"required": [
"theme"
],
"properties": {
"theme": {
"type": "string",
"maxLength": 255
},
"theme_config": {
"type": "object",
"additionalProperties": true
}
}
},
"schema.SiteThemeResp": {
"type": "object",
"required": [
"theme"
],
"properties": {
"theme": {
"type": "string",
"maxLength": 255
},
"theme_config": {
"type": "object",
"additionalProperties": true
}
}
},
"schema.SiteWriteReq": {
"type": "object",
"properties": {
@ -6942,6 +7372,23 @@
}
}
},
"schema.UpdateUserPasswordReq": {
"type": "object",
"required": [
"password",
"user_id"
],
"properties": {
"password": {
"type": "string",
"maxLength": 32,
"minLength": 8
},
"user_id": {
"type": "string"
}
}
},
"schema.UpdateUserRoleReq": {
"type": "object",
"required": [

View File

@ -176,6 +176,23 @@ definitions:
- object_id
- report_type
type: object
schema.AddUserReq:
properties:
display_name:
maxLength: 30
type: string
email:
maxLength: 500
type: string
password:
maxLength: 32
minLength: 8
type: string
required:
- display_name
- email
- password
type: object
schema.AdminSetAnswerStatusRequest:
properties:
answer_id:
@ -1189,6 +1206,36 @@ definitions:
- logo
- square_icon
type: object
schema.SiteCustomCssHTMLReq:
properties:
custom_css:
maxLength: 65536
type: string
custom_footer:
maxLength: 65536
type: string
custom_head:
maxLength: 65536
type: string
custom_header:
maxLength: 65536
type: string
type: object
schema.SiteCustomCssHTMLResp:
properties:
custom_css:
maxLength: 65536
type: string
custom_footer:
maxLength: 65536
type: string
custom_head:
maxLength: 65536
type: string
custom_header:
maxLength: 65536
type: string
type: object
schema.SiteGeneralReq:
properties:
contact_email:
@ -1243,6 +1290,19 @@ definitions:
- permalink
- site_url
type: object
schema.SiteInfoResp:
properties:
branding:
$ref: '#/definitions/schema.SiteBrandingResp'
general:
$ref: '#/definitions/schema.SiteGeneralResp'
interface:
$ref: '#/definitions/schema.SiteInterfaceResp'
login:
$ref: '#/definitions/schema.SiteLoginResp'
theme:
$ref: '#/definitions/schema.SiteThemeResp'
type: object
schema.SiteInterfaceReq:
properties:
language:
@ -1297,6 +1357,20 @@ definitions:
terms_of_service_parsed_text:
type: string
type: object
schema.SiteLoginReq:
properties:
allow_new_registrations:
type: boolean
login_required:
type: boolean
type: object
schema.SiteLoginResp:
properties:
allow_new_registrations:
type: boolean
login_required:
type: boolean
type: object
schema.SiteSeoReq:
properties:
robots:
@ -1311,6 +1385,28 @@ definitions:
required:
- robots
type: object
schema.SiteThemeReq:
properties:
theme:
maxLength: 255
type: string
theme_config:
additionalProperties: true
type: object
required:
- theme
type: object
schema.SiteThemeResp:
properties:
theme:
maxLength: 255
type: string
theme_config:
additionalProperties: true
type: object
required:
- theme
type: object
schema.SiteWriteReq:
properties:
recommend_tags:
@ -1533,6 +1629,18 @@ definitions:
required:
- language
type: object
schema.UpdateUserPasswordReq:
properties:
password:
maxLength: 32
minLength: 8
type: string
user_id:
type: string
required:
- password
- user_id
type: object
schema.UpdateUserRoleReq:
properties:
role_id:
@ -2102,6 +2210,47 @@ paths:
summary: update site info branding
tags:
- admin
/answer/admin/api/siteinfo/custom-css-html:
get:
description: get site info custom html css config
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/handler.RespBody'
- properties:
data:
$ref: '#/definitions/schema.SiteCustomCssHTMLResp'
type: object
security:
- ApiKeyAuth: []
summary: get site info custom html css config
tags:
- admin
put:
description: update site custom css html config
parameters:
- description: login info
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.SiteCustomCssHTMLReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: update site custom css html config
tags:
- admin
/answer/admin/api/siteinfo/general:
get:
description: get site general information
@ -2225,6 +2374,47 @@ paths:
summary: update site legal info
tags:
- admin
/answer/admin/api/siteinfo/login:
get:
description: get site info login config
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/handler.RespBody'
- properties:
data:
$ref: '#/definitions/schema.SiteLoginResp'
type: object
security:
- ApiKeyAuth: []
summary: get site info login config
tags:
- admin
put:
description: update site login
parameters:
- description: login info
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.SiteLoginReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: update site login
tags:
- admin
/answer/admin/api/siteinfo/seo:
get:
description: get site seo information
@ -2266,6 +2456,47 @@ paths:
summary: update site seo information
tags:
- admin
/answer/admin/api/siteinfo/theme:
get:
description: get site info theme config
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/handler.RespBody'
- properties:
data:
$ref: '#/definitions/schema.SiteThemeResp'
type: object
security:
- ApiKeyAuth: []
summary: get site info theme config
tags:
- admin
put:
description: update site custom css html config
parameters:
- description: login info
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.SiteThemeReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: update site custom css html config
tags:
- admin
/answer/admin/api/siteinfo/write:
get:
description: get site interface
@ -2322,6 +2553,54 @@ paths:
summary: Get theme options
tags:
- admin
/answer/admin/api/user:
post:
consumes:
- application/json
description: add user
parameters:
- description: user
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.AddUserReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: add user
tags:
- admin
/answer/admin/api/user/password:
put:
consumes:
- application/json
description: update user password
parameters:
- description: user
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.UpdateUserPasswordReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: update user password
tags:
- admin
/answer/admin/api/user/role:
put:
consumes:
@ -3824,7 +4103,7 @@ paths:
- $ref: '#/definitions/handler.RespBody'
- properties:
data:
$ref: '#/definitions/schema.SiteGeneralResp'
$ref: '#/definitions/schema.SiteInfoResp'
type: object
summary: get site info
tags:

View File

@ -145,6 +145,8 @@ backend:
other: "Cant create the config.yaml file."
cannot_update_your_role:
other: "You cannot modify your role."
not_allowed_registration:
other: "Currently the site is not open for registration"
report:
spam:
name:

View File

@ -117,6 +117,8 @@ backend:
other: "头像设置错误"
cannot_update_your_role:
other: "你无法修改自己的角色"
not_allowed_registration:
other: "目前该网站尚未开放注册"
revision:
review_underway:
other: "目前无法编辑,有一个版本在审阅队列中。"

View File

@ -12,6 +12,8 @@ const (
AdminTokenCacheTime = 7 * 24 * time.Hour
AcceptLanguageFlag = "Accept-Language"
UserTokenMappingCacheKey = "answer:user-token:mapping:"
SiteInfoCacheKey = "answer:site-info:"
SiteInfoCacheTime = 1 * time.Hour
)
const (
@ -54,12 +56,15 @@ var (
)
const (
SiteTypeGeneral = "general"
SiteTypeInterface = "interface"
SiteTypeBranding = "branding"
SiteTypeWrite = "write"
SiteTypeLegal = "legal"
SiteTypeSeo = "seo"
SiteTypeGeneral = "general"
SiteTypeInterface = "interface"
SiteTypeBranding = "branding"
SiteTypeWrite = "write"
SiteTypeLegal = "legal"
SiteTypeSeo = "seo"
SiteTypeLogin = "login"
SiteTypeCustomCssHTML = "css-html"
SiteTypeTheme = "theme"
)
func ExistInPathIgnore(name string) bool {

View File

@ -4,6 +4,7 @@ import (
"strings"
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service/siteinfo_common"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/reason"
@ -18,13 +19,17 @@ var ctxUUIDKey = "ctxUuidKey"
// AuthUserMiddleware auth user middleware
type AuthUserMiddleware struct {
authService *auth.AuthService
authService *auth.AuthService
siteInfoCommonService *siteinfo_common.SiteInfoCommonService
}
// NewAuthUserMiddleware new auth user middleware
func NewAuthUserMiddleware(authService *auth.AuthService) *AuthUserMiddleware {
func NewAuthUserMiddleware(
authService *auth.AuthService,
siteInfoCommonService *siteinfo_common.SiteInfoCommonService) *AuthUserMiddleware {
return &AuthUserMiddleware{
authService: authService,
authService: authService,
siteInfoCommonService: siteInfoCommonService,
}
}
@ -48,6 +53,29 @@ func (am *AuthUserMiddleware) Auth() gin.HandlerFunc {
}
}
// EjectUserBySiteInfo if admin config the site can access by nologin user, eject user.
func (am *AuthUserMiddleware) EjectUserBySiteInfo() gin.HandlerFunc {
return func(ctx *gin.Context) {
mustLogin := false
siteInfo, _ := am.siteInfoCommonService.GetSiteLogin(ctx)
if siteInfo != nil {
mustLogin = siteInfo.LoginRequired
}
if !mustLogin {
ctx.Next()
return
}
_, isLogin := ctx.Get(ctxUUIDKey)
if !isLogin {
handler.HandleResponse(ctx, errors.Unauthorized(reason.UnauthorizedError), nil)
ctx.Abort()
return
}
ctx.Next()
}
}
// MustAuth auth user info. If the user does not log in, an unauthenticated error is displayed
func (am *AuthUserMiddleware) MustAuth() gin.HandlerFunc {
return func(ctx *gin.Context) {

View File

@ -59,4 +59,5 @@ const (
RevisionNoPermission = "error.revision.no_permission"
UserCannotUpdateYourRole = "error.user.cannot_update_your_role"
TagCannotSetSynonymAsItself = "error.tag.cannot_set_synonym_as_itself"
NotAllowedRegistration = "error.user.not_allowed_registration"
)

View File

@ -52,9 +52,13 @@ func NewHTTPServer(debug bool,
static.Use(avatarMiddleware.AvatarThumb())
staticRouter.RegisterStaticRouter(static)
// The route must be available without logging in
mustUnAuthV1 := r.Group("/answer/api/v1")
answerRouter.RegisterMustUnAuthAnswerAPIRouter(mustUnAuthV1)
// register api that no need to login
unAuthV1 := r.Group("/answer/api/v1")
unAuthV1.Use(authUserMiddleware.Auth())
unAuthV1.Use(authUserMiddleware.Auth(), authUserMiddleware.EjectUserBySiteInfo())
answerRouter.RegisterUnAuthAnswerAPIRouter(unAuthV1)
// register api that must be authenticated

View File

@ -27,7 +27,7 @@ func NewSiteinfoController(siteInfoService *siteinfo_common.SiteInfoCommonServic
// @Description get site info
// @Tags site
// @Produce json
// @Success 200 {object} handler.RespBody{data=schema.SiteGeneralResp}
// @Success 200 {object} handler.RespBody{data=schema.SiteInfoResp}
// @Router /answer/api/v1/siteinfo [get]
func (sc *SiteinfoController) GetSiteInfo(ctx *gin.Context) {
var err error
@ -45,6 +45,21 @@ func (sc *SiteinfoController) GetSiteInfo(ctx *gin.Context) {
if err != nil {
log.Error(err)
}
resp.Login, err = sc.siteInfoService.GetSiteLogin(ctx)
if err != nil {
log.Error(err)
}
resp.Theme, err = sc.siteInfoService.GetSiteTheme(ctx)
if err != nil {
log.Error(err)
}
resp.CustomCssHtml, err = sc.siteInfoService.GetSiteCustomCssHTML(ctx)
if err != nil {
log.Error(err)
}
handler.HandleResponse(ctx, nil, resp)
}

View File

@ -11,6 +11,7 @@ import (
"github.com/answerdev/answer/internal/service/action"
"github.com/answerdev/answer/internal/service/auth"
"github.com/answerdev/answer/internal/service/export"
"github.com/answerdev/answer/internal/service/siteinfo_common"
"github.com/answerdev/answer/internal/service/uploader"
"github.com/gin-gonic/gin"
"github.com/segmentfault/pacman/errors"
@ -18,11 +19,12 @@ import (
// UserController user controller
type UserController struct {
userService *service.UserService
authService *auth.AuthService
actionService *action.CaptchaService
uploaderService *uploader.UploaderService
emailService *export.EmailService
userService *service.UserService
authService *auth.AuthService
actionService *action.CaptchaService
uploaderService *uploader.UploaderService
emailService *export.EmailService
siteInfoCommonService *siteinfo_common.SiteInfoCommonService
}
// NewUserController new controller
@ -32,13 +34,15 @@ func NewUserController(
actionService *action.CaptchaService,
emailService *export.EmailService,
uploaderService *uploader.UploaderService,
siteInfoCommonService *siteinfo_common.SiteInfoCommonService,
) *UserController {
return &UserController{
authService: authService,
userService: userService,
actionService: actionService,
uploaderService: uploaderService,
emailService: emailService,
authService: authService,
userService: userService,
actionService: actionService,
uploaderService: uploaderService,
emailService: emailService,
siteInfoCommonService: siteInfoCommonService,
}
}
@ -203,6 +207,17 @@ func (uc *UserController) UserLogout(ctx *gin.Context) {
// @Success 200 {object} handler.RespBody{data=schema.GetUserResp}
// @Router /answer/api/v1/user/register/email [post]
func (uc *UserController) UserRegisterByEmail(ctx *gin.Context) {
// check whether site allow register or not
siteInfo, err := uc.siteInfoCommonService.GetSiteLogin(ctx)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !siteInfo.AllowNewRegistrations {
handler.HandleResponse(ctx, errors.BadRequest(reason.NotAllowedRegistration), nil)
return
}
req := &schema.UserRegisterReq{}
if handler.BindAndCheck(ctx, req) {
return
@ -473,3 +488,17 @@ func (uc *UserController) UserChangeEmailVerify(ctx *gin.Context) {
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
handler.HandleResponse(ctx, err, nil)
}
// UserRanking get user ranking
// @Summary get user ranking
// @Description get user ranking
// @Tags User
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Success 200 {object} handler.RespBody{data=schema.GetUserToSetShowResp}
// @Router /answer/api/v1/user/ranking [get]
func (uc *UserController) UserRanking(ctx *gin.Context) {
resp, err := uc.userService.UserRanking(ctx)
handler.HandleResponse(ctx, err, resp)
}

View File

@ -100,6 +100,45 @@ func (sc *SiteInfoController) GetSeo(ctx *gin.Context) {
handler.HandleResponse(ctx, err, resp)
}
// GetSiteLogin get site info login config
// @Summary get site info login config
// @Description get site info login config
// @Security ApiKeyAuth
// @Tags admin
// @Produce json
// @Success 200 {object} handler.RespBody{data=schema.SiteLoginResp}
// @Router /answer/admin/api/siteinfo/login [get]
func (sc *SiteInfoController) GetSiteLogin(ctx *gin.Context) {
resp, err := sc.siteInfoService.GetSiteLogin(ctx)
handler.HandleResponse(ctx, err, resp)
}
// GetSiteCustomCssHTML get site info custom html css config
// @Summary get site info custom html css config
// @Description get site info custom html css config
// @Security ApiKeyAuth
// @Tags admin
// @Produce json
// @Success 200 {object} handler.RespBody{data=schema.SiteCustomCssHTMLResp}
// @Router /answer/admin/api/siteinfo/custom-css-html [get]
func (sc *SiteInfoController) GetSiteCustomCssHTML(ctx *gin.Context) {
resp, err := sc.siteInfoService.GetSiteCustomCssHTML(ctx)
handler.HandleResponse(ctx, err, resp)
}
// GetSiteTheme get site info theme config
// @Summary get site info theme config
// @Description get site info theme config
// @Security ApiKeyAuth
// @Tags admin
// @Produce json
// @Success 200 {object} handler.RespBody{data=schema.SiteThemeResp}
// @Router /answer/admin/api/siteinfo/theme [get]
func (sc *SiteInfoController) GetSiteTheme(ctx *gin.Context) {
resp, err := sc.siteInfoService.GetSiteTheme(ctx)
handler.HandleResponse(ctx, err, resp)
}
// GetRobots get site robots information
// @Summary get site robots information
// @Description get site robots information
@ -226,6 +265,60 @@ func (sc *SiteInfoController) UpdateSiteLegal(ctx *gin.Context) {
handler.HandleResponse(ctx, err, nil)
}
// UpdateSiteLogin update site login
// @Summary update site login
// @Description update site login
// @Security ApiKeyAuth
// @Tags admin
// @Produce json
// @Param data body schema.SiteLoginReq true "login info"
// @Success 200 {object} handler.RespBody{}
// @Router /answer/admin/api/siteinfo/login [put]
func (sc *SiteInfoController) UpdateSiteLogin(ctx *gin.Context) {
req := &schema.SiteLoginReq{}
if handler.BindAndCheck(ctx, req) {
return
}
err := sc.siteInfoService.SaveSiteLogin(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// UpdateSiteCustomCssHTML update site custom css html config
// @Summary update site custom css html config
// @Description update site custom css html config
// @Security ApiKeyAuth
// @Tags admin
// @Produce json
// @Param data body schema.SiteCustomCssHTMLReq true "login info"
// @Success 200 {object} handler.RespBody{}
// @Router /answer/admin/api/siteinfo/custom-css-html [put]
func (sc *SiteInfoController) UpdateSiteCustomCssHTML(ctx *gin.Context) {
req := &schema.SiteCustomCssHTMLReq{}
if handler.BindAndCheck(ctx, req) {
return
}
err := sc.siteInfoService.SaveSiteCustomCssHTML(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// SaveSiteTheme update site custom css html config
// @Summary update site custom css html config
// @Description update site custom css html config
// @Security ApiKeyAuth
// @Tags admin
// @Produce json
// @Param data body schema.SiteThemeReq true "login info"
// @Success 200 {object} handler.RespBody{}
// @Router /answer/admin/api/siteinfo/theme [put]
func (sc *SiteInfoController) SaveSiteTheme(ctx *gin.Context) {
req := &schema.SiteThemeReq{}
if handler.BindAndCheck(ctx, req) {
return
}
err := sc.siteInfoService.SaveSiteTheme(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// GetSMTPConfig get smtp config
// @Summary GetSMTPConfig get smtp config
// @Description GetSMTPConfig get smtp config

View File

@ -60,6 +60,50 @@ func (uc *UserBackyardController) UpdateUserRole(ctx *gin.Context) {
handler.HandleResponse(ctx, err, nil)
}
// AddUser add user
// @Summary add user
// @Description add user
// @Security ApiKeyAuth
// @Tags admin
// @Accept json
// @Produce json
// @Param data body schema.AddUserReq true "user"
// @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/user [post]
func (uc *UserBackyardController) AddUser(ctx *gin.Context) {
req := &schema.AddUserReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
err := uc.userService.AddUser(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// UpdateUserPassword update user password
// @Summary update user password
// @Description update user password
// @Security ApiKeyAuth
// @Tags admin
// @Accept json
// @Produce json
// @Param data body schema.UpdateUserPasswordReq true "user"
// @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/user/password [put]
func (uc *UserBackyardController) UpdateUserPassword(ctx *gin.Context) {
req := &schema.UpdateUserPasswordReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
err := uc.userService.UpdateUserPassword(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// GetUserPage get user page
// @Summary get user page
// @Description get user page

View File

@ -44,8 +44,8 @@ type User struct {
Website string `xorm:"not null default '' VARCHAR(255) website"`
Location string `xorm:"not null default '' VARCHAR(100) location"`
IPInfo string `xorm:"not null default '' VARCHAR(255) ip_info"`
//IsAdmin bool `xorm:"not null default false BOOL is_admin"`
Language string `xorm:"not null default '' VARCHAR(100) language"`
IsAdmin bool `xorm:"not null default false BOOL is_admin"`
Language string `xorm:"not null default '' VARCHAR(100) language"`
}
// TableName user table name

View File

@ -46,6 +46,7 @@ var migrations = []Migration{
NewMigration("add recommend and reserved tag fields", addTagRecommendedAndReserved),
NewMigration("add activity timeline", addActivityTimeline),
NewMigration("add user role", addRoleFeatures),
NewMigration("add theme and private mode", addThemeAndPrivateMode),
}
// GetCurrentDBVersion returns the current db version

22
internal/migrations/v5.go Normal file
View File

@ -0,0 +1,22 @@
package migrations
import (
"encoding/json"
"github.com/answerdev/answer/internal/entity"
"xorm.io/xorm"
)
func addThemeAndPrivateMode(x *xorm.Engine) error {
loginConfig := map[string]bool{
"allow_new_registrations": true,
"login_required": false,
}
loginConfigDataBytes, _ := json.Marshal(loginConfig)
_, err := x.InsertOne(&entity.SiteInfo{
Type: "login",
Content: string(loginConfigDataBytes),
Status: 1,
})
return err
}

View File

@ -3,6 +3,7 @@ package activity_common
import (
"context"
"fmt"
"time"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/service/activity_common"
@ -106,3 +107,9 @@ func (ar *ActivityRepo) AddActivity(ctx context.Context, activity *entity.Activi
}
return
}
// GetUsersWhoHasGainedTheMostReputation get users who has gained the most reputation over a period of time
func (ar *ActivityRepo) GetUsersWhoHasGainedTheMostReputation(
ctx context.Context, startTime, endTime time.Time, limit int) (userIDs []string, err error) {
return
}

View File

@ -2,12 +2,15 @@ package site_info
import (
"context"
"encoding/json"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/service/siteinfo_common"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
"xorm.io/builder"
)
@ -23,32 +26,55 @@ func NewSiteInfo(data *data.Data) siteinfo_common.SiteInfoRepo {
// SaveByType save site setting by type
func (sr *siteInfoRepo) SaveByType(ctx context.Context, siteType string, data *entity.SiteInfo) (err error) {
var (
old = &entity.SiteInfo{}
exist bool
)
exist, _ = sr.data.DB.Where(builder.Eq{"type": siteType}).Get(old)
old := &entity.SiteInfo{}
exist, err := sr.data.DB.Where(builder.Eq{"type": siteType}).Get(old)
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
if exist {
_, err = sr.data.DB.ID(old.ID).Update(data)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
} else {
_, err = sr.data.DB.Insert(data)
}
_, err = sr.data.DB.Insert(data)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
sr.setCache(ctx, siteType, data)
return
}
// GetByType get site info by type
func (sr *siteInfoRepo) GetByType(ctx context.Context, siteType string) (siteInfo *entity.SiteInfo, exist bool, err error) {
siteInfo = sr.getCache(ctx, siteType)
if siteInfo != nil {
return siteInfo, true, nil
}
siteInfo = &entity.SiteInfo{}
exist, err = sr.data.DB.Where(builder.Eq{"type": siteType}).Get(siteInfo)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
if exist {
sr.setCache(ctx, siteType, siteInfo)
}
return
}
func (sr *siteInfoRepo) getCache(ctx context.Context, siteType string) (siteInfo *entity.SiteInfo) {
siteInfo = &entity.SiteInfo{}
siteInfoCache, err := sr.data.Cache.GetString(ctx, constant.SiteInfoCacheKey+siteType)
if err != nil {
return nil
}
_ = json.Unmarshal([]byte(siteInfoCache), siteInfo)
return siteInfo
}
func (sr *siteInfoRepo) setCache(ctx context.Context, siteType string, siteInfo *entity.SiteInfo) {
siteInfoCache, _ := json.Marshal(siteInfo)
err := sr.data.Cache.SetString(ctx,
constant.SiteInfoCacheKey+siteType, string(siteInfoCache), constant.SiteInfoCacheTime)
if err != nil {
log.Error(err)
}
}

View File

@ -61,6 +61,24 @@ func (ur *userBackyardRepo) UpdateUserStatus(ctx context.Context, userID string,
return
}
// AddUser add user
func (ur *userBackyardRepo) AddUser(ctx context.Context, user *entity.User) (err error) {
_, err = ur.data.DB.Insert(user)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// UpdateUserPassword update user password
func (ur *userBackyardRepo) UpdateUserPassword(ctx context.Context, userID string, password string) (err error) {
_, err = ur.data.DB.ID(userID).Update(&entity.User{Pass: password})
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetUserInfo get user info
func (ur *userBackyardRepo) GetUserInfo(ctx context.Context, userID string) (user *entity.User, exist bool, err error) {
user = &entity.User{}

View File

@ -87,11 +87,17 @@ func NewAnswerAPIRouter(
}
}
func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
func (a *AnswerAPIRouter) RegisterMustUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
// i18n
r.GET("/language/config", a.langController.GetLangMapping)
r.GET("/language/options", a.langController.GetUserLangOptions)
//siteinfo
r.GET("/siteinfo", a.siteinfoController.GetSiteInfo)
r.GET("/siteinfo/legal", a.siteinfoController.GetSiteLegalInfo)
}
func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
// comment
r.GET("/comment/page", a.commentController.GetCommentWithPage)
r.GET("/personal/comment/page", a.commentController.GetCommentPersonalWithPage)
@ -110,6 +116,7 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
r.GET("/user/logout", a.userController.UserLogout)
r.PUT("/user/email", a.userController.UserChangeEmailVerify)
r.POST("/user/email/change/code", a.userController.UserChangeEmailSendCode)
r.GET("/user/ranking", a.userController.UserRanking)
//answer
r.GET("/answer/info", a.answerController.Get)
@ -139,11 +146,6 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
//rank
r.GET("/personal/rank/page", a.rankController.GetRankPersonalWithPage)
//siteinfo
r.GET("/siteinfo", a.siteinfoController.GetSiteInfo)
r.GET("/siteinfo/legal", a.siteinfoController.GetSiteLegalInfo)
}
func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
@ -234,6 +236,8 @@ func (a *AnswerAPIRouter) RegisterAnswerCmsAPIRouter(r *gin.RouterGroup) {
r.GET("/users/page", a.backyardUserController.GetUserPage)
r.PUT("/user/status", a.backyardUserController.UpdateUserStatus)
r.PUT("/user/role", a.backyardUserController.UpdateUserRole)
r.POST("/user", a.backyardUserController.AddUser)
r.PUT("/user/password", a.backyardUserController.UpdateUserPassword)
// reason
r.GET("/reasons", a.reasonController.Reasons)
@ -251,11 +255,17 @@ func (a *AnswerAPIRouter) RegisterAnswerCmsAPIRouter(r *gin.RouterGroup) {
r.GET("/siteinfo/write", a.siteInfoController.GetSiteWrite)
r.GET("/siteinfo/legal", a.siteInfoController.GetSiteLegal)
r.GET("/siteinfo/seo", a.siteInfoController.GetSeo)
r.GET("/siteinfo/login", a.siteInfoController.GetSiteLogin)
r.GET("/siteinfo/custom-css-html", a.siteInfoController.GetSiteCustomCssHTML)
r.GET("/siteinfo/theme", a.siteInfoController.GetSiteTheme)
r.PUT("/siteinfo/general", a.siteInfoController.UpdateGeneral)
r.PUT("/siteinfo/interface", a.siteInfoController.UpdateInterface)
r.PUT("/siteinfo/branding", a.siteInfoController.UpdateBranding)
r.PUT("/siteinfo/write", a.siteInfoController.UpdateSiteWrite)
r.PUT("/siteinfo/legal", a.siteInfoController.UpdateSiteLegal)
r.PUT("/siteinfo/login", a.siteInfoController.UpdateSiteLogin)
r.PUT("/siteinfo/custom-css-html", a.siteInfoController.UpdateSiteCustomCssHTML)
r.PUT("/siteinfo/theme", a.siteInfoController.SaveSiteTheme)
r.PUT("/siteinfo/seo", a.siteInfoController.UpdateSeo)
r.GET("/setting/smtp", a.siteInfoController.GetSMTPConfig)
r.PUT("/setting/smtp", a.siteInfoController.UpdateSMTPConfig)

View File

@ -84,3 +84,18 @@ type UpdateUserRoleReq struct {
// login user id
LoginUserID string `json:"-"`
}
// AddUserReq add user request
type AddUserReq struct {
DisplayName string `validate:"required,gt=4,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:"-"`
}
// 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:"-"`
}

View File

@ -82,6 +82,26 @@ type GetSiteLegalInfoResp struct {
PrivacyPolicyParsedText string `json:"privacy_policy_parsed_text,omitempty"`
}
// SiteLoginReq site login request
type SiteLoginReq struct {
AllowNewRegistrations bool `json:"allow_new_registrations"`
LoginRequired bool `json:"login_required"`
}
// 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"`
}
// SiteThemeReq site theme config
type SiteThemeReq struct {
Theme string `validate:"required,gt=0,lte=255" json:"theme"`
ThemeConfig map[string]interface{} `validate:"omitempty" json:"theme_config"`
}
// SiteGeneralResp site general response
type SiteGeneralResp SiteGeneralReq
@ -91,19 +111,32 @@ type SiteInterfaceResp SiteInterfaceReq
// SiteBrandingResp site branding response
type SiteBrandingResp SiteBrandingReq
// SiteLoginResp site login response
type SiteLoginResp SiteLoginReq
// SiteCustomCssHTMLResp site custom css html response
type SiteCustomCssHTMLResp SiteCustomCssHTMLReq
// SiteThemeResp site theme response
type SiteThemeResp SiteThemeReq
// SiteWriteResp site write response
type SiteWriteResp SiteWriteReq
// SiteLegalResp site write response
type SiteLegalResp SiteLegalReq
// SiteSeoResp site write response
type SiteSeoResp SiteSeoReq
// SiteInfoResp get site info response
type SiteInfoResp struct {
General *SiteGeneralResp `json:"general"`
Interface *SiteInterfaceResp `json:"interface"`
Branding *SiteBrandingResp `json:"branding"`
General *SiteGeneralResp `json:"general"`
Interface *SiteInterfaceResp `json:"interface"`
Branding *SiteBrandingResp `json:"branding"`
Login *SiteLoginResp `json:"login"`
Theme *SiteThemeResp `json:"theme"`
CustomCssHtml *SiteCustomCssHTMLResp `json:"custom_css_html"`
}
type TemplateSiteInfoResp struct {
General *SiteGeneralResp `json:"general"`

View File

@ -11,12 +11,4 @@ var GetThemeOptions = []*GetThemeOption{
Label: "Default",
Value: "default",
},
{
Label: "Black",
Value: "black",
},
{
Label: "White",
Value: "white",
},
}

View File

@ -417,3 +417,22 @@ type UserVerifyEmailSendReq struct {
CaptchaID string `validate:"omitempty,gt=0,lte=500" json:"captcha_id"`
CaptchaCode string `validate:"omitempty,gt=0,lte=500" 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"`
// display name
DisplayName string `json:"display_name"`
// avatar
Avatar string `json:"avatar"`
}

View File

@ -18,65 +18,38 @@ import (
)
type SiteInfoService struct {
siteInfoRepo siteinfo_common.SiteInfoRepo
emailService *export.EmailService
tagCommonService *tagcommon.TagCommonService
siteInfoRepo siteinfo_common.SiteInfoRepo
siteInfoCommonService *siteinfo_common.SiteInfoCommonService
emailService *export.EmailService
tagCommonService *tagcommon.TagCommonService
}
func NewSiteInfoService(
siteInfoRepo siteinfo_common.SiteInfoRepo,
siteInfoCommonService *siteinfo_common.SiteInfoCommonService,
emailService *export.EmailService,
tagCommonService *tagcommon.TagCommonService) *SiteInfoService {
return &SiteInfoService{
siteInfoRepo: siteInfoRepo,
emailService: emailService,
tagCommonService: tagCommonService,
siteInfoRepo: siteInfoRepo,
siteInfoCommonService: siteInfoCommonService,
emailService: emailService,
tagCommonService: tagCommonService,
}
}
// GetSiteGeneral get site info general
func (s *SiteInfoService) GetSiteGeneral(ctx context.Context) (resp *schema.SiteGeneralResp, err error) {
resp = &schema.SiteGeneralResp{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeGeneral)
if err != nil {
log.Error(err)
return resp, nil
}
if !exist {
return resp, nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return resp, nil
return s.siteInfoCommonService.GetSiteGeneral(ctx)
}
// GetSiteInterface get site info interface
func (s *SiteInfoService) GetSiteInterface(ctx context.Context) (resp *schema.SiteInterfaceResp, err error) {
resp = &schema.SiteInterfaceResp{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeInterface)
if err != nil {
log.Error(err)
return resp, nil
}
if !exist {
return resp, nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return resp, nil
return s.siteInfoCommonService.GetSiteInterface(ctx)
}
// GetSiteBranding get site info branding
func (s *SiteInfoService) GetSiteBranding(ctx context.Context) (resp *schema.SiteBrandingReq, err error) {
resp = &schema.SiteBrandingReq{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeBranding)
if err != nil {
log.Error(err)
return resp, nil
}
if !exist {
return resp, nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return resp, nil
func (s *SiteInfoService) GetSiteBranding(ctx context.Context) (resp *schema.SiteBrandingResp, err error) {
return s.siteInfoCommonService.GetSiteBranding(ctx)
}
// GetSiteWrite get site info write
@ -104,16 +77,22 @@ func (s *SiteInfoService) GetSiteWrite(ctx context.Context) (resp *schema.SiteWr
// GetSiteLegal get site legal info
func (s *SiteInfoService) GetSiteLegal(ctx context.Context) (resp *schema.SiteLegalResp, err error) {
resp = &schema.SiteLegalResp{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeLegal)
if err != nil {
return nil, err
}
if !exist {
return resp, nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return resp, nil
return s.siteInfoCommonService.GetSiteLegal(ctx)
}
// GetSiteLogin get site login info
func (s *SiteInfoService) GetSiteLogin(ctx context.Context) (resp *schema.SiteLoginResp, err error) {
return s.siteInfoCommonService.GetSiteLogin(ctx)
}
// GetSiteCustomCssHTML get site custom css html config
func (s *SiteInfoService) GetSiteCustomCssHTML(ctx context.Context) (resp *schema.SiteCustomCssHTMLResp, err error) {
return s.siteInfoCommonService.GetSiteCustomCssHTML(ctx)
}
// GetSiteTheme get site theme config
func (s *SiteInfoService) GetSiteTheme(ctx context.Context) (resp *schema.SiteThemeResp, err error) {
return s.siteInfoCommonService.GetSiteTheme(ctx)
}
func (s *SiteInfoService) SaveSiteGeneral(ctx context.Context, req schema.SiteGeneralReq) (err error) {
@ -207,6 +186,39 @@ func (s *SiteInfoService) SaveSiteLegal(ctx context.Context, req *schema.SiteLeg
return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeLegal, data)
}
// SaveSiteLogin save site legal configuration
func (s *SiteInfoService) SaveSiteLogin(ctx context.Context, req *schema.SiteLoginReq) (err error) {
content, _ := json.Marshal(req)
data := &entity.SiteInfo{
Type: constant.SiteTypeLogin,
Content: string(content),
Status: 1,
}
return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeLogin, data)
}
// SaveSiteCustomCssHTML save site custom html configuration
func (s *SiteInfoService) SaveSiteCustomCssHTML(ctx context.Context, req *schema.SiteCustomCssHTMLReq) (err error) {
content, _ := json.Marshal(req)
data := &entity.SiteInfo{
Type: constant.SiteTypeCustomCssHTML,
Content: string(content),
Status: 1,
}
return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeCustomCssHTML, data)
}
// SaveSiteTheme save site custom html configuration
func (s *SiteInfoService) SaveSiteTheme(ctx context.Context, req *schema.SiteThemeReq) (err error) {
content, _ := json.Marshal(req)
data := &entity.SiteInfo{
Type: constant.SiteTypeTheme,
Content: string(content),
Status: 1,
}
return s.siteInfoRepo.SaveByType(ctx, constant.SiteTypeTheme, data)
}
// GetSMTPConfig get smtp config
func (s *SiteInfoService) GetSMTPConfig(ctx context.Context) (
resp *schema.GetSMTPConfigResp, err error,
@ -243,6 +255,18 @@ func (s *SiteInfoService) UpdateSMTPConfig(ctx context.Context, req *schema.Upda
}
func (s *SiteInfoService) GetSeo(ctx context.Context) (resp *schema.SiteSeoResp, err error) {
resp = &schema.SiteSeoResp{}
loginConfig, err := s.GetSiteLogin(ctx)
if err != nil {
log.Error(err)
return resp, nil
}
// If the site is set to privacy mode, prohibit crawling any page.
if loginConfig.LoginRequired {
resp.Robots = "User-agent: *\nDisallow: /"
return resp, nil
}
resp = &schema.SiteSeoResp{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeSeo)
if err != nil {

View File

@ -1,12 +0,0 @@
package siteinfo_common
import (
"context"
"github.com/answerdev/answer/internal/entity"
)
type SiteInfoRepo interface {
SaveByType(ctx context.Context, siteType string, data *entity.SiteInfo) (err error)
GetByType(ctx context.Context, siteType string) (siteInfo *entity.SiteInfo, exist bool, err error)
}

View File

@ -5,13 +5,21 @@ import (
"encoding/json"
"github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
)
type SiteInfoRepo interface {
SaveByType(ctx context.Context, siteType string, data *entity.SiteInfo) (err error)
GetByType(ctx context.Context, siteType string) (siteInfo *entity.SiteInfo, exist bool, err error)
}
// SiteInfoCommonService site info common service
type SiteInfoCommonService struct {
siteInfoRepo SiteInfoRepo
}
// NewSiteInfoCommonService new site info common service
func NewSiteInfoCommonService(siteInfoRepo SiteInfoRepo) *SiteInfoCommonService {
return &SiteInfoCommonService{
siteInfoRepo: siteInfoRepo,
@ -21,69 +29,83 @@ func NewSiteInfoCommonService(siteInfoRepo SiteInfoRepo) *SiteInfoCommonService
// GetSiteGeneral get site info general
func (s *SiteInfoCommonService) GetSiteGeneral(ctx context.Context) (resp *schema.SiteGeneralResp, err error) {
resp = &schema.SiteGeneralResp{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeGeneral)
if err != nil {
return resp, err
if err = s.getSiteInfoByType(ctx, constant.SiteTypeGeneral, resp); err != nil {
return nil, err
}
if !exist {
return resp, nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return resp, nil
}
// GetSiteInterface get site info interface
func (s *SiteInfoCommonService) GetSiteInterface(ctx context.Context) (resp *schema.SiteInterfaceResp, err error) {
resp = &schema.SiteInterfaceResp{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeInterface)
if err != nil {
return resp, err
if err = s.getSiteInfoByType(ctx, constant.SiteTypeInterface, resp); err != nil {
return nil, err
}
if !exist {
return resp, nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return resp, nil
}
// GetSiteBranding get site info branding
func (s *SiteInfoCommonService) GetSiteBranding(ctx context.Context) (resp *schema.SiteBrandingResp, err error) {
resp = &schema.SiteBrandingResp{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeBranding)
if err != nil {
return resp, err
if err = s.getSiteInfoByType(ctx, constant.SiteTypeBranding, resp); err != nil {
return nil, err
}
if !exist {
return resp, nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return resp, nil
}
// GetSiteWrite get site info write
func (s *SiteInfoCommonService) GetSiteWrite(ctx context.Context) (resp *schema.SiteWriteResp, err error) {
resp = &schema.SiteWriteResp{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeWrite)
if err != nil {
return resp, err
if err = s.getSiteInfoByType(ctx, constant.SiteTypeWrite, resp); err != nil {
return nil, err
}
if !exist {
return resp, nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return resp, nil
}
// GetSiteLegal get site info write
func (s *SiteInfoCommonService) GetSiteLegal(ctx context.Context) (resp *schema.SiteLegalResp, err error) {
resp = &schema.SiteLegalResp{}
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeLegal)
if err != nil {
if err = s.getSiteInfoByType(ctx, constant.SiteTypeLegal, resp); err != nil {
return nil, err
}
if !exist {
return resp, nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return resp, nil
}
// GetSiteLogin get site login config
func (s *SiteInfoCommonService) GetSiteLogin(ctx context.Context) (resp *schema.SiteLoginResp, err error) {
resp = &schema.SiteLoginResp{}
if err = s.getSiteInfoByType(ctx, constant.SiteTypeLogin, resp); err != nil {
return nil, err
}
return resp, nil
}
// GetSiteCustomCssHTML get site custom css html config
func (s *SiteInfoCommonService) GetSiteCustomCssHTML(ctx context.Context) (resp *schema.SiteCustomCssHTMLResp, err error) {
resp = &schema.SiteCustomCssHTMLResp{}
if err = s.getSiteInfoByType(ctx, constant.SiteTypeCustomCssHTML, resp); err != nil {
return nil, err
}
return resp, nil
}
// GetSiteTheme get site theme
func (s *SiteInfoCommonService) GetSiteTheme(ctx context.Context) (resp *schema.SiteThemeResp, err error) {
resp = &schema.SiteThemeResp{}
if err = s.getSiteInfoByType(ctx, constant.SiteTypeTheme, resp); err != nil {
return nil, err
}
return resp, nil
}
func (s *SiteInfoCommonService) getSiteInfoByType(ctx context.Context, siteType string, resp interface{}) (err error) {
siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, siteType)
if err != nil {
return err
}
if !exist {
return nil
}
_ = json.Unmarshal([]byte(siteInfo.Content), resp)
return nil
}

View File

@ -14,9 +14,11 @@ import (
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service/auth"
"github.com/answerdev/answer/internal/service/role"
usercommon "github.com/answerdev/answer/internal/service/user_common"
"github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
"golang.org/x/crypto/bcrypt"
)
// UserBackyardRepo user repository
@ -25,6 +27,8 @@ type UserBackyardRepo interface {
GetUserInfo(ctx context.Context, userID string) (user *entity.User, exist bool, err error)
GetUserPage(ctx context.Context, page, pageSize int, user *entity.User,
usernameOrDisplayName string, isStaff bool) (users []*entity.User, total int64, err error)
AddUser(ctx context.Context, user *entity.User) (err error)
UpdateUserPassword(ctx context.Context, userID string, password string) (err error)
}
// UserBackyardService user service
@ -32,6 +36,7 @@ type UserBackyardService struct {
userRepo UserBackyardRepo
userRoleRelService *role.UserRoleRelService
authService *auth.AuthService
userCommonService *usercommon.UserCommon
}
// NewUserBackyardService new user backyard service
@ -39,11 +44,13 @@ func NewUserBackyardService(
userRepo UserBackyardRepo,
userRoleRelService *role.UserRoleRelService,
authService *auth.AuthService,
userCommonService *usercommon.UserCommon,
) *UserBackyardService {
return &UserBackyardService{
userRepo: userRepo,
userRoleRelService: userRoleRelService,
authService: authService,
userCommonService: userCommonService,
}
}
@ -94,6 +101,57 @@ func (us *UserBackyardService) UpdateUserRole(ctx context.Context, req *schema.U
return
}
// AddUser add user
func (us *UserBackyardService) AddUser(ctx context.Context, req *schema.AddUserReq) (err error) {
hashPwd, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return err
}
userInfo := &entity.User{}
userInfo.EMail = req.Email
userInfo.DisplayName = req.DisplayName
userInfo.Pass = string(hashPwd)
userInfo.Username, err = us.userCommonService.MakeUsername(ctx, userInfo.DisplayName)
if err != nil {
return err
}
userInfo.MailStatus = entity.EmailStatusAvailable
userInfo.Status = entity.UserStatusAvailable
userInfo.Rank = 1
err = us.userRepo.AddUser(ctx, userInfo)
if err != nil {
return err
}
return
}
// UpdateUserPassword update user password
func (us *UserBackyardService) UpdateUserPassword(ctx context.Context, req *schema.UpdateUserPasswordReq) (err error) {
userInfo, exist, err := us.userRepo.GetUserInfo(ctx, req.UserID)
if err != nil {
return err
}
if !exist {
return errors.BadRequest(reason.UserNotFound)
}
hashPwd, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return err
}
err = us.userRepo.UpdateUserPassword(ctx, userInfo.ID, string(hashPwd))
if err != nil {
return err
}
// logout this user
us.authService.RemoveAllUserTokens(ctx, req.UserID)
return
}
// GetUserInfo get user one
func (us *UserBackyardService) GetUserInfo(ctx context.Context, userID string) (resp *schema.GetUserInfoResp, err error) {
user, exist, err := us.userRepo.GetUserInfo(ctx, userID)

View File

@ -2,9 +2,17 @@ package usercommon
import (
"context"
"encoding/hex"
"math/rand"
"regexp"
"strings"
"github.com/Chain-Zhang/pinyin"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/pkg/checker"
"github.com/segmentfault/pacman/errors"
)
type UserRepo interface {
@ -94,3 +102,41 @@ func (us *UserCommon) FormatUserBasicInfo(ctx context.Context, userInfo *entity.
}
return userBasicInfo
}
// MakeUsername
// Generate a unique Username based on the displayName
func (us *UserCommon) MakeUsername(ctx context.Context, displayName string) (username string, err error) {
// Chinese processing
if has := checker.IsChinese(displayName); has {
str, err := pinyin.New(displayName).Split("").Mode(pinyin.WithoutTone).Convert()
if err != nil {
return "", errors.BadRequest(reason.UsernameInvalid)
} else {
displayName = str
}
}
username = strings.ReplaceAll(displayName, " ", "_")
username = strings.ToLower(username)
suffix := ""
re := regexp.MustCompile(`^[a-z0-9._-]{4,30}$`)
match := re.MatchString(username)
if !match {
return "", errors.BadRequest(reason.UsernameInvalid)
}
for {
_, has, err := us.userRepo.GetByUsername(ctx, username+suffix)
if err != nil {
return "", err
}
if !has {
break
}
bytes := make([]byte, 2)
_, _ = rand.Read(bytes)
suffix = hex.EncodeToString(bytes)
}
return username + suffix, nil
}

View File

@ -2,14 +2,9 @@ package service
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
"regexp"
"strings"
"github.com/Chain-Zhang/pinyin"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/base/translator"
@ -23,7 +18,6 @@ import (
"github.com/answerdev/answer/internal/service/service_config"
"github.com/answerdev/answer/internal/service/siteinfo_common"
usercommon "github.com/answerdev/answer/internal/service/user_common"
"github.com/answerdev/answer/pkg/checker"
"github.com/google/uuid"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
@ -34,13 +28,14 @@ import (
// UserService user service
type UserService struct {
userRepo usercommon.UserRepo
userActivity activity.UserActiveActivityRepo
serviceConfig *service_config.ServiceConfig
emailService *export.EmailService
authService *auth.AuthService
siteInfoService *siteinfo_common.SiteInfoCommonService
userRoleService *role.UserRoleRelService
userCommonService *usercommon.UserCommon
userRepo usercommon.UserRepo
userActivity activity.UserActiveActivityRepo
serviceConfig *service_config.ServiceConfig
emailService *export.EmailService
authService *auth.AuthService
siteInfoService *siteinfo_common.SiteInfoCommonService
userRoleService *role.UserRoleRelService
}
func NewUserService(userRepo usercommon.UserRepo,
@ -50,15 +45,17 @@ func NewUserService(userRepo usercommon.UserRepo,
serviceConfig *service_config.ServiceConfig,
siteInfoService *siteinfo_common.SiteInfoCommonService,
userRoleService *role.UserRoleRelService,
userCommonService *usercommon.UserCommon,
) *UserService {
return &UserService{
userRepo: userRepo,
userActivity: userActivity,
emailService: emailService,
serviceConfig: serviceConfig,
authService: authService,
siteInfoService: siteInfoService,
userRoleService: userRoleService,
userCommonService: userCommonService,
userRepo: userRepo,
userActivity: userActivity,
emailService: emailService,
serviceConfig: serviceConfig,
authService: authService,
siteInfoService: siteInfoService,
userRoleService: userRoleService,
}
}
@ -307,7 +304,7 @@ func (us *UserService) UserRegisterByEmail(ctx context.Context, registerUserInfo
if err != nil {
return nil, err
}
userInfo.Username, err = us.makeUsername(ctx, registerUserInfo.Name)
userInfo.Username, err = us.userCommonService.MakeUsername(ctx, registerUserInfo.Name)
if err != nil {
return nil, err
}
@ -456,44 +453,6 @@ func (us *UserService) UserVerifyEmail(ctx context.Context, req *schema.UserVeri
return resp, nil
}
// makeUsername
// Generate a unique Username based on the displayName
func (us *UserService) makeUsername(ctx context.Context, displayName string) (username string, err error) {
// Chinese processing
if has := checker.IsChinese(displayName); has {
str, err := pinyin.New(displayName).Split("").Mode(pinyin.WithoutTone).Convert()
if err != nil {
return "", err
} else {
displayName = str
}
}
username = strings.ReplaceAll(displayName, " ", "_")
username = strings.ToLower(username)
suffix := ""
re := regexp.MustCompile(`^[a-z0-9._-]{4,30}$`)
match := re.MatchString(username)
if !match {
return "", errors.BadRequest(reason.UsernameInvalid)
}
for {
_, has, err := us.userRepo.GetByUsername(ctx, username+suffix)
if err != nil {
return "", err
}
if !has {
break
}
bytes := make([]byte, 2)
_, _ = rand.Read(bytes)
suffix = hex.EncodeToString(bytes)
}
return username + suffix, nil
}
// verifyPassword
// Compare whether the password is correct
func (us *UserService) verifyPassword(ctx context.Context, LoginPass, UserPass string) bool {
@ -596,3 +555,9 @@ func (us *UserService) getSiteUrl(ctx context.Context) string {
}
return siteGeneral.SiteUrl
}
// UserRanking get user ranking
func (us *UserService) UserRanking(ctx context.Context) (resp []*schema.UserRankingResp, err error) {
//us.userRepo.GetByUserID()
return
}