mirror of https://gitee.com/answerdev/answer.git
feat(seo): seo update
This commit is contained in:
commit
c09d861b5d
|
@ -9,6 +9,7 @@
|
|||
/.fleet
|
||||
/.vscode/*.log
|
||||
/cmd/answer/*.sh
|
||||
/cmd/answer/answer
|
||||
/cmd/answer/uploads/*
|
||||
/cmd/logs
|
||||
/configs/config-dev.yaml
|
||||
|
|
|
@ -45,6 +45,7 @@ func runApp() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
conf.GetPathIgnoreList()
|
||||
app, cleanup, err := initApplication(
|
||||
c.Debug, c.Server, c.Data.Database, c.Data.Cache, c.I18n, c.Swaggerui, c.ServiceConfig, log.GetLogger())
|
||||
if err != nil {
|
||||
|
|
|
@ -4,3 +4,6 @@ import _ "embed"
|
|||
|
||||
//go:embed config.yaml
|
||||
var Config []byte
|
||||
|
||||
//go:embed path_ignore.yaml
|
||||
var PathIgnore []byte
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# url path reserves the keywords list
|
||||
users:
|
||||
- settings
|
||||
- login
|
||||
- register
|
||||
- account-recovery
|
||||
- change-email
|
||||
- password-reset
|
||||
- account-activation
|
||||
- confirm-new-email
|
||||
- account-suspended
|
234
docs/docs.go
234
docs/docs.go
|
@ -822,77 +822,6 @@ 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": [
|
||||
|
@ -1060,84 +989,6 @@ 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": [
|
||||
|
@ -3605,7 +3456,7 @@ const docTemplate = `{
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/schema.SiteInfoResp"
|
||||
"$ref": "#/definitions/schema.SiteGeneralResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5208,29 +5059,6 @@ 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": {
|
||||
|
@ -5896,6 +5724,10 @@ const docTemplate = `{
|
|||
"description": "created time",
|
||||
"type": "integer"
|
||||
},
|
||||
"description": {
|
||||
"description": "description text",
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"description": "display name",
|
||||
"type": "string"
|
||||
|
@ -6710,23 +6542,6 @@ 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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.SiteInterfaceReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -6805,28 +6620,6 @@ 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": [
|
||||
|
@ -7161,23 +6954,6 @@ 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": [
|
||||
|
|
|
@ -810,77 +810,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/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": [
|
||||
|
@ -1048,84 +977,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/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": [
|
||||
|
@ -3593,7 +3444,7 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/schema.SiteInfoResp"
|
||||
"$ref": "#/definitions/schema.SiteGeneralResp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5196,29 +5047,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
|
@ -5884,6 +5712,10 @@
|
|||
"description": "created time",
|
||||
"type": "integer"
|
||||
},
|
||||
"description": {
|
||||
"description": "description text",
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"description": "display name",
|
||||
"type": "string"
|
||||
|
@ -6698,23 +6530,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.SiteInterfaceReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -6793,28 +6608,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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": [
|
||||
|
@ -7149,23 +6942,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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": [
|
||||
|
|
|
@ -176,23 +176,6 @@ 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:
|
||||
|
@ -667,6 +650,9 @@ definitions:
|
|||
created_at:
|
||||
description: created time
|
||||
type: integer
|
||||
description:
|
||||
description: description text
|
||||
type: string
|
||||
display_name:
|
||||
description: display name
|
||||
type: string
|
||||
|
@ -1257,17 +1243,6 @@ 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'
|
||||
type: object
|
||||
schema.SiteInterfaceReq:
|
||||
properties:
|
||||
language:
|
||||
|
@ -1322,20 +1297,6 @@ 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:
|
||||
|
@ -1572,18 +1533,6 @@ 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:
|
||||
|
@ -2276,47 +2225,6 @@ 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
|
||||
|
@ -2414,54 +2322,6 @@ 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:
|
||||
|
@ -3964,7 +3824,7 @@ paths:
|
|||
- $ref: '#/definitions/handler.RespBody'
|
||||
- properties:
|
||||
data:
|
||||
$ref: '#/definitions/schema.SiteInfoResp'
|
||||
$ref: '#/definitions/schema.SiteGeneralResp'
|
||||
type: object
|
||||
summary: get site info
|
||||
tags:
|
||||
|
|
|
@ -19,14 +19,14 @@ backend:
|
|||
other: "User"
|
||||
admin:
|
||||
other: "Admin"
|
||||
Moderator:
|
||||
moderator:
|
||||
other: "Moderator"
|
||||
description:
|
||||
user:
|
||||
other: "Default with no special access."
|
||||
admin:
|
||||
other: "Have the full power to access the site."
|
||||
Moderator:
|
||||
moderator:
|
||||
other: "Has access to all posts except admin settings."
|
||||
|
||||
email:
|
||||
|
@ -109,6 +109,8 @@ backend:
|
|||
other: "Should not contain synonym tags."
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
cannot_set_synonym_as_itself:
|
||||
other: "You cannot set the synonym of the current tag as itself."
|
||||
theme:
|
||||
not_found:
|
||||
other: "Theme not found."
|
||||
|
|
|
@ -97,6 +97,8 @@ backend:
|
|||
other: "不应包含同义词标签。"
|
||||
cannot_update:
|
||||
other: "没有更新标签权限。"
|
||||
cannot_set_synonym_as_itself:
|
||||
other: "你无法将当前标签的同义词设置为当前标签自己"
|
||||
theme:
|
||||
not_found:
|
||||
other: "主题未找到"
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"bytes"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/answerdev/answer/configs"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/data"
|
||||
"github.com/answerdev/answer/internal/base/server"
|
||||
"github.com/answerdev/answer/internal/base/translator"
|
||||
|
@ -12,6 +14,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/service_config"
|
||||
"github.com/answerdev/answer/pkg/writer"
|
||||
"github.com/segmentfault/pacman/contrib/conf/viper"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
@ -25,6 +28,10 @@ type AllConfig struct {
|
|||
Swaggerui *router.SwaggerConfig `json:"swaggerui" mapstructure:"swaggerui" yaml:"swaggerui"`
|
||||
}
|
||||
|
||||
type PathIgnore struct {
|
||||
Users []string `yaml:"users"`
|
||||
}
|
||||
|
||||
// Server server config
|
||||
type Server struct {
|
||||
HTTP *server.HTTP `json:"http" mapstructure:"http" yaml:"http"`
|
||||
|
@ -62,3 +69,18 @@ func RewriteConfig(configFilePath string, allConfig *AllConfig) error {
|
|||
}
|
||||
return writer.ReplaceFile(configFilePath, buf.String())
|
||||
}
|
||||
|
||||
func GetPathIgnoreList() map[string]bool {
|
||||
list := make(map[string]bool, 0)
|
||||
data := &PathIgnore{}
|
||||
err := yaml.Unmarshal(configs.PathIgnore, data)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return list
|
||||
}
|
||||
for _, item := range data.Users {
|
||||
list[item] = true
|
||||
}
|
||||
constant.PathIgnoreMap = list
|
||||
return list
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ const (
|
|||
var (
|
||||
Version string = ""
|
||||
|
||||
PathIgnoreMap map[string]bool
|
||||
|
||||
ObjectTypeStrMapping = map[string]int{
|
||||
QuestionObjectType: 1,
|
||||
AnswerObjectType: 2,
|
||||
|
@ -62,3 +64,11 @@ const (
|
|||
SiteTypeSeo = "seo"
|
||||
SiteTypeLogin = "login"
|
||||
)
|
||||
|
||||
func ExistInPathIgnore(name string) bool {
|
||||
_, ok := PathIgnoreMap[name]
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -58,5 +58,6 @@ const (
|
|||
RevisionReviewUnderway = "error.revision.review_underway"
|
||||
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"
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"io/fs"
|
||||
"math"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -69,20 +70,41 @@ func NewHTTPServer(debug bool,
|
|||
answerRouter.RegisterAnswerCmsAPIRouter(cmsauthV1)
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"replaceHTMLTag": func(src string, tags ...string) string {
|
||||
p := `(?U)<(\d+)>.+</(\d+)>`
|
||||
|
||||
re := regexp.MustCompile(p)
|
||||
ms := re.FindAllStringSubmatch(src, -1)
|
||||
for _, mi := range ms {
|
||||
if mi[1] == mi[2] {
|
||||
i, err := strconv.Atoi(mi[1])
|
||||
if err != nil || len(tags) < i {
|
||||
break
|
||||
}
|
||||
|
||||
src = strings.ReplaceAll(src, mi[0], tags[i-1])
|
||||
}
|
||||
}
|
||||
|
||||
return src
|
||||
},
|
||||
"join": func(sep string, elems ...string) string {
|
||||
return strings.Join(elems, sep)
|
||||
},
|
||||
"templateHTML": func(data string) template.HTML {
|
||||
return template.HTML(data)
|
||||
},
|
||||
"translator": func(la i18n.Language, data string, params ...interface{}) string {
|
||||
// todo
|
||||
/*if len(params) > 0 && len(params)%2 == 0 {
|
||||
trans := translator.GlobalTrans.Tr(la, data)
|
||||
|
||||
if len(params) > 0 && len(params)%2 == 0 {
|
||||
for i := 0; i < len(params); i += 2 {
|
||||
k := converter.InterfaceToString(params[i])
|
||||
v := converter.InterfaceToString(params[i+1])
|
||||
data = strings.ReplaceAll(data, "{{ "+k+" }}", v)
|
||||
trans = strings.ReplaceAll(trans, "{{ "+k+" }}", v)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
trans := translator.GlobalTrans.Tr(la, data)
|
||||
return trans
|
||||
},
|
||||
"timeFormatISO": func(tz string, timestamp int64) string {
|
||||
|
@ -120,7 +142,7 @@ func NewHTTPServer(debug bool,
|
|||
}
|
||||
|
||||
if between >= 3600 && between < 3600*24 {
|
||||
h := math.Floor(float64(between / 60))
|
||||
h := math.Floor(float64(between / 3600))
|
||||
trans = translator.GlobalTrans.Tr(la, "ui.dates.x_hours_ago")
|
||||
return strings.ReplaceAll(trans, "{{count}}", strconv.FormatFloat(h, 'f', 0, 64))
|
||||
}
|
||||
|
|
|
@ -296,6 +296,7 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) {
|
|||
permission.QuestionEdit,
|
||||
permission.QuestionDelete,
|
||||
permission.QuestionEditWithoutReview,
|
||||
permission.TagUseReservedTag,
|
||||
}, req.ID)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
|
@ -304,16 +305,18 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) {
|
|||
req.CanEdit = canList[0]
|
||||
req.CanDelete = canList[1]
|
||||
req.NoNeedReview = canList[2]
|
||||
|
||||
req.CanClose = middleware.GetIsAdminFromContext(ctx)
|
||||
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
|
||||
req.CanUseReservedTag = canList[3]
|
||||
if !req.CanEdit {
|
||||
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = qc.questionService.UpdateQuestion(ctx, req)
|
||||
handler.HandleResponse(ctx, err, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview})
|
||||
resp, err := qc.questionService.UpdateQuestion(ctx, req)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
return
|
||||
}
|
||||
handler.HandleResponse(ctx, nil, &schema.UpdateQuestionResp{WaitForReview: !req.NoNeedReview})
|
||||
}
|
||||
|
||||
// CloseMsgList close question msg list
|
||||
|
|
|
@ -3,6 +3,7 @@ package controller
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/siteinfo_common"
|
||||
|
@ -83,13 +84,17 @@ func (sc *SiteinfoController) GetSiteLegalInfo(ctx *gin.Context) {
|
|||
|
||||
// GetManifestJson get manifest.json
|
||||
func (sc *SiteinfoController) GetManifestJson(ctx *gin.Context) {
|
||||
favicon := "favicon.ico"
|
||||
resp := &schema.GetManifestJsonResp{
|
||||
ShortName: "Answer",
|
||||
Name: "Answer.dev",
|
||||
ManifestVersion: 3,
|
||||
Version: constant.Version,
|
||||
ShortName: "Answer",
|
||||
Name: "Answer.dev",
|
||||
Icons: map[string]string{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon",
|
||||
"16": favicon,
|
||||
"32": favicon,
|
||||
"48": favicon,
|
||||
"128": favicon,
|
||||
},
|
||||
StartUrl: ".",
|
||||
Display: "standalone",
|
||||
|
@ -100,7 +105,10 @@ func (sc *SiteinfoController) GetManifestJson(ctx *gin.Context) {
|
|||
if err != nil {
|
||||
log.Error(err)
|
||||
} else if len(branding.Favicon) > 0 {
|
||||
resp.Icons["scr"] = branding.Favicon
|
||||
resp.Icons["16"] = branding.Favicon
|
||||
resp.Icons["32"] = branding.Favicon
|
||||
resp.Icons["48"] = branding.Favicon
|
||||
resp.Icons["128"] = branding.Favicon
|
||||
}
|
||||
ctx.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
templaterender "github.com/answerdev/answer/internal/controller/template_render"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
|
@ -78,7 +79,9 @@ func (tc *TemplateController) SiteInfo(ctx *gin.Context) *schema.TemplateSiteInf
|
|||
|
||||
// Index question list
|
||||
func (tc *TemplateController) Index(ctx *gin.Context) {
|
||||
req := &schema.QuestionSearch{}
|
||||
req := &schema.QuestionSearch{
|
||||
Order: "newest",
|
||||
}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
tc.Page404(ctx)
|
||||
return
|
||||
|
@ -101,7 +104,9 @@ func (tc *TemplateController) Index(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
func (tc *TemplateController) QuestionList(ctx *gin.Context) {
|
||||
req := &schema.QuestionSearch{}
|
||||
req := &schema.QuestionSearch{
|
||||
Order: "newest",
|
||||
}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
tc.Page404(ctx)
|
||||
return
|
||||
|
@ -261,15 +266,23 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) {
|
|||
|
||||
// UserInfo user info
|
||||
func (tc *TemplateController) UserInfo(ctx *gin.Context) {
|
||||
// urlPath := ctx.Request.URL.Path
|
||||
// filePath := ""
|
||||
// switch urlPath {
|
||||
// case "/users/login":
|
||||
// filePath = "build/index.html"
|
||||
// case "/users/register":
|
||||
// filePath = "build/index.html"
|
||||
// default:
|
||||
username := ctx.Param("username")
|
||||
if username == "" {
|
||||
tc.Page404(ctx)
|
||||
return
|
||||
}
|
||||
exist := constant.ExistInPathIgnore(username)
|
||||
if exist {
|
||||
file, err := ui.Build.ReadFile("build/index.html")
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
tc.Page404(ctx)
|
||||
return
|
||||
}
|
||||
ctx.Header("content-type", "text/html;charset=utf-8")
|
||||
ctx.String(http.StatusOK, string(file))
|
||||
return
|
||||
}
|
||||
req := &schema.GetOtherUserInfoByUsernameReq{}
|
||||
req.Username = username
|
||||
userinfo, err := tc.templateRenderController.UserInfo(ctx, req)
|
||||
|
@ -288,16 +301,6 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) {
|
|||
"userinfo": userinfo,
|
||||
"bio": template.HTML(userinfo.Info.BioHTML),
|
||||
})
|
||||
// }
|
||||
|
||||
// file, err := ui.Build.ReadFile(filePath)
|
||||
// if err != nil {
|
||||
// log.Error(err)
|
||||
// ctx.Status(http.StatusNotFound)
|
||||
// return
|
||||
// }
|
||||
// ctx.Header("content-type", "text/html;charset=utf-8")
|
||||
// ctx.String(http.StatusOK, string(file))
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/service/permission"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
|
@ -118,6 +121,7 @@ func addRoleFeatures(x *xorm.Engine) error {
|
|||
{RoleID: 2, PowerType: permission.AnswerAudit},
|
||||
{RoleID: 2, PowerType: permission.QuestionAudit},
|
||||
{RoleID: 2, PowerType: permission.TagAudit},
|
||||
{RoleID: 2, PowerType: permission.TagUseReservedTag},
|
||||
|
||||
{RoleID: 3, PowerType: permission.QuestionAdd},
|
||||
{RoleID: 3, PowerType: permission.QuestionEdit},
|
||||
|
@ -151,6 +155,7 @@ func addRoleFeatures(x *xorm.Engine) error {
|
|||
{RoleID: 3, PowerType: permission.AnswerAudit},
|
||||
{RoleID: 3, PowerType: permission.QuestionAudit},
|
||||
{RoleID: 3, PowerType: permission.TagAudit},
|
||||
{RoleID: 3, PowerType: permission.TagUseReservedTag},
|
||||
}
|
||||
|
||||
// insert default powers
|
||||
|
@ -183,5 +188,28 @@ func addRoleFeatures(x *xorm.Engine) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfigTable := []*entity.Config{
|
||||
{ID: 115, Key: "rank.question.close", Value: `-1`},
|
||||
{ID: 116, Key: "rank.question.reopen", Value: `-1`},
|
||||
{ID: 117, Key: "rank.tag.use_reserved_tag", Value: `-1`},
|
||||
}
|
||||
for _, c := range defaultConfigTable {
|
||||
exist, err := x.Get(&entity.Config{ID: c.ID, Key: c.Key})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get config failed: %w", err)
|
||||
}
|
||||
if exist {
|
||||
if _, err = x.Update(c, &entity.Config{ID: c.ID, Key: c.Key}); err != nil {
|
||||
log.Errorf("update %+v config failed: %s", c, err)
|
||||
return fmt.Errorf("update config failed: %w", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, err = x.Insert(&entity.Config{ID: c.ID, Key: c.Key, Value: c.Value}); err != nil {
|
||||
log.Errorf("insert %+v config failed: %s", c, err)
|
||||
return fmt.Errorf("add config failed: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func (tr *tagCommonRepo) GetTagBySlugName(ctx context.Context, slugName string)
|
|||
}
|
||||
|
||||
// GetTagListByName get tag list all like name
|
||||
func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, limit int, hasReserved bool) (tagList []*entity.Tag, err error) {
|
||||
func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, hasReserved bool) (tagList []*entity.Tag, err error) {
|
||||
tagList = make([]*entity.Tag, 0)
|
||||
cond := &entity.Tag{}
|
||||
session := tr.data.DB.Where("")
|
||||
|
@ -65,10 +65,7 @@ func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, limi
|
|||
cond.Recommend = true
|
||||
}
|
||||
session.Where(builder.Eq{"status": entity.TagStatusAvailable})
|
||||
// if limit == 0 {
|
||||
// session.Asc("slug_name")
|
||||
// }
|
||||
session.Limit(limit).Asc("slug_name")
|
||||
session.Asc("slug_name")
|
||||
// if !hasReserved {
|
||||
// cond.Reserved = false
|
||||
// session.UseBool("recommend", "reserved")
|
||||
|
|
|
@ -41,6 +41,6 @@ func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup) {
|
|||
|
||||
r.GET("/tags", a.templateController.TagList)
|
||||
r.GET("/tags/:tag", a.templateController.TagInfo)
|
||||
// r.GET("/users/:username", a.templateController.UserInfo)
|
||||
r.GET("/users/:username", a.templateController.UserInfo)
|
||||
r.GET("/404", a.templateController.Page404)
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@ type QuestionPermission struct {
|
|||
CanClose bool `json:"-"`
|
||||
// whether user can reopen it
|
||||
CanReopen bool `json:"-"`
|
||||
// whether user can use reserved it
|
||||
CanUseReservedTag bool `json:"-"`
|
||||
}
|
||||
|
||||
type CheckCanQuestionUpdate struct {
|
||||
|
@ -76,7 +78,6 @@ type QuestionUpdate struct {
|
|||
EditSummary string `validate:"omitempty" json:"edit_summary"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
NoNeedReview bool `json:"-"`
|
||||
QuestionPermission
|
||||
}
|
||||
|
@ -97,6 +98,7 @@ type QuestionInfo struct {
|
|||
Title string `json:"title" xorm:"title"` // title
|
||||
Content string `json:"content" xorm:"content"` // content
|
||||
HTML string `json:"html" xorm:"html"` // html
|
||||
Description string `json:"description"` //description
|
||||
Tags []*TagResp `json:"tags" ` // tags
|
||||
ViewCount int `json:"view_count" xorm:"view_count"` // view_count
|
||||
UniqueViewCount int `json:"unique_view_count" xorm:"unique_view_count"` // unique_view_count
|
||||
|
|
|
@ -153,6 +153,8 @@ type GetSMTPConfigResp struct {
|
|||
|
||||
// GetManifestJsonResp get manifest json response
|
||||
type GetManifestJsonResp struct {
|
||||
ManifestVersion int `json:"manifest_version"`
|
||||
Version string `json:"version"`
|
||||
ShortName string `json:"short_name"`
|
||||
Name string `json:"name"`
|
||||
Icons map[string]string `json:"icons"`
|
||||
|
|
|
@ -66,6 +66,8 @@ type GetTagResp struct {
|
|||
OriginalText string `json:"original_text"`
|
||||
// parsed text
|
||||
ParsedText string `json:"parsed_text"`
|
||||
// description text
|
||||
Description string `json:"description"`
|
||||
// follower amount
|
||||
FollowCount int `json:"follow_count"`
|
||||
// question amount
|
||||
|
|
|
@ -34,4 +34,5 @@ const (
|
|||
AnswerAudit = "answer.audit"
|
||||
QuestionAudit = "question.audit"
|
||||
TagAudit = "tag.audit"
|
||||
TagUseReservedTag = "tag.use_reserved_tag"
|
||||
)
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/revision_common"
|
||||
tagcommon "github.com/answerdev/answer/internal/service/tag_common"
|
||||
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
||||
"github.com/answerdev/answer/pkg/htmltext"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
|
@ -351,14 +352,23 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest
|
|||
return questionInfo, tagerr
|
||||
}
|
||||
|
||||
// If it's not admin
|
||||
if !req.IsAdmin {
|
||||
//CheckChangeTag
|
||||
|
||||
CheckTag, CheckTaglist := qs.CheckChangeReservedTag(ctx, oldTags, Tags)
|
||||
if !CheckTag {
|
||||
// if user can not use reserved tag, old reserved tag can not be removed and new reserved tag can not be added.
|
||||
if !req.CanUseReservedTag {
|
||||
CheckOldTag, CheckNewTag, CheckOldTaglist, CheckNewTaglist := qs.CheckChangeReservedTag(ctx, oldTags, Tags)
|
||||
if !CheckOldTag {
|
||||
errMsg := fmt.Sprintf(`The reserved tag "%s" must be present.`,
|
||||
strings.Join(CheckTaglist, ","))
|
||||
strings.Join(CheckOldTaglist, ","))
|
||||
errorlist := make([]*validator.FormErrorField, 0)
|
||||
errorlist = append(errorlist, &validator.FormErrorField{
|
||||
ErrorField: "tags",
|
||||
ErrorMsg: errMsg,
|
||||
})
|
||||
err = errors.BadRequest(reason.RequestFormatError).WithMsg(errMsg)
|
||||
return errorlist, err
|
||||
}
|
||||
if !CheckNewTag {
|
||||
errMsg := fmt.Sprintf(`"%s" can only be used by moderators.`,
|
||||
strings.Join(CheckNewTaglist, ","))
|
||||
errorlist := make([]*validator.FormErrorField, 0)
|
||||
errorlist = append(errorlist, &validator.FormErrorField{
|
||||
ErrorField: "tags",
|
||||
|
@ -392,7 +402,7 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest
|
|||
Log: req.EditSummary,
|
||||
}
|
||||
|
||||
if req.NoNeedReview || req.IsAdmin || dbinfo.UserID == req.UserID {
|
||||
if req.NoNeedReview {
|
||||
canUpdate = true
|
||||
}
|
||||
|
||||
|
@ -454,6 +464,7 @@ func (qs *QuestionService) GetQuestion(ctx context.Context, questionID, userID s
|
|||
if question.Status == entity.QuestionStatusClosed {
|
||||
per.CanClose = false
|
||||
}
|
||||
question.Description = htmltext.FetchExcerpt(question.HTML, "...", 240)
|
||||
question.MemberActions = permission.GetQuestionPermission(ctx, userID, question.UserID,
|
||||
per.CanEdit, per.CanDelete, per.CanClose, per.CanReopen)
|
||||
return question, nil
|
||||
|
@ -474,7 +485,7 @@ func (qs *QuestionService) ChangeTag(ctx context.Context, objectTagData *schema.
|
|||
return qs.tagCommon.ObjectChangeTag(ctx, objectTagData)
|
||||
}
|
||||
|
||||
func (qs *QuestionService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, []string) {
|
||||
func (qs *QuestionService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, bool, []string, []string) {
|
||||
return qs.tagCommon.CheckChangeReservedTag(ctx, oldobjectTagData, objectTagData)
|
||||
}
|
||||
|
||||
|
|
|
@ -150,16 +150,10 @@ func (rs *RankService) CheckVotePermission(ctx context.Context, userID, objectID
|
|||
if !exist {
|
||||
return can, nil
|
||||
}
|
||||
// TODO administrator have all permissions
|
||||
//if userInfo.IsAdmin {
|
||||
// return true, nil
|
||||
//}
|
||||
|
||||
objectInfo, err := rs.objectInfoService.GetInfo(ctx, objectID)
|
||||
if err != nil {
|
||||
return can, err
|
||||
}
|
||||
|
||||
action := ""
|
||||
switch objectInfo.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
|
@ -181,6 +175,11 @@ func (rs *RankService) CheckVotePermission(ctx context.Context, userID, objectID
|
|||
action = permission.CommentVoteDown
|
||||
}
|
||||
}
|
||||
powerMapping := rs.getUserPowerMapping(ctx, userID)
|
||||
if powerMapping[action] {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
meetRank := rs.checkUserRank(ctx, userInfo.ID, userInfo.Rank, PermissionPrefix+action)
|
||||
return meetRank, nil
|
||||
}
|
||||
|
|
|
@ -105,6 +105,7 @@ func (ts *TagService) GetTagInfo(ctx context.Context, req *schema.GetTagInfoReq)
|
|||
resp.DisplayName = tagInfo.DisplayName
|
||||
resp.OriginalText = tagInfo.OriginalText
|
||||
resp.ParsedText = tagInfo.ParsedText
|
||||
resp.Description = htmltext.FetchExcerpt(tagInfo.ParsedText, "...", 240)
|
||||
resp.FollowCount = tagInfo.FollowCount
|
||||
resp.QuestionCount = tagInfo.QuestionCount
|
||||
resp.Recommend = tagInfo.Recommend
|
||||
|
@ -215,6 +216,9 @@ func (ts *TagService) UpdateTagSynonym(ctx context.Context, req *schema.UpdateTa
|
|||
|
||||
// find all exist tag
|
||||
for _, item := range req.SynonymTagList {
|
||||
if item.SlugName == mainTagInfo.SlugName {
|
||||
return errors.BadRequest(reason.TagCannotSetSynonymAsItself)
|
||||
}
|
||||
addSynonymTagList = append(addSynonymTagList, item.SlugName)
|
||||
}
|
||||
tagListInDB, err := ts.tagCommonService.GetTagListByNames(ctx, addSynonymTagList)
|
||||
|
|
|
@ -24,7 +24,7 @@ type TagCommonRepo interface {
|
|||
AddTagList(ctx context.Context, tagList []*entity.Tag) (err error)
|
||||
GetTagListByIDs(ctx context.Context, ids []string) (tagList []*entity.Tag, err error)
|
||||
GetTagBySlugName(ctx context.Context, slugName string) (tagInfo *entity.Tag, exist bool, err error)
|
||||
GetTagListByName(ctx context.Context, name string, limit int, hasReserved bool) (tagList []*entity.Tag, err error)
|
||||
GetTagListByName(ctx context.Context, name string, hasReserved bool) (tagList []*entity.Tag, err error)
|
||||
GetTagListByNames(ctx context.Context, names []string) (tagList []*entity.Tag, err error)
|
||||
GetTagByID(ctx context.Context, tagID string, includeDeleted bool) (tag *entity.Tag, exist bool, err error)
|
||||
GetTagPage(ctx context.Context, page, pageSize int, tag *entity.Tag, queryCond string) (tagList []*entity.Tag, total int64, err error)
|
||||
|
@ -79,7 +79,7 @@ func NewTagCommonService(
|
|||
|
||||
// SearchTagLike get tag list all
|
||||
func (ts *TagCommonService) SearchTagLike(ctx context.Context, req *schema.SearchTagLikeReq) (resp []schema.SearchTagLikeResp, err error) {
|
||||
tags, err := ts.tagCommonRepo.GetTagListByName(ctx, req.Tag, 0, req.IsAdmin)
|
||||
tags, err := ts.tagCommonRepo.GetTagListByName(ctx, req.Tag, req.IsAdmin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -443,9 +443,10 @@ func (ts *TagCommonService) CheckTagsIsChange(ctx context.Context, tagNameList,
|
|||
return false
|
||||
}
|
||||
|
||||
func (ts *TagCommonService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, []string) {
|
||||
func (ts *TagCommonService) CheckChangeReservedTag(ctx context.Context, oldobjectTagData, objectTagData []*entity.Tag) (bool, bool, []string, []string) {
|
||||
reservedTagsMap := make(map[string]bool)
|
||||
needTagsMap := make([]string, 0)
|
||||
notNeedTagsMap := make([]string, 0)
|
||||
for _, tag := range objectTagData {
|
||||
if tag.Reserved {
|
||||
reservedTagsMap[tag.SlugName] = true
|
||||
|
@ -456,14 +457,27 @@ func (ts *TagCommonService) CheckChangeReservedTag(ctx context.Context, oldobjec
|
|||
_, ok := reservedTagsMap[tag.SlugName]
|
||||
if !ok {
|
||||
needTagsMap = append(needTagsMap, tag.SlugName)
|
||||
} else {
|
||||
reservedTagsMap[tag.SlugName] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(needTagsMap) > 0 {
|
||||
return false, needTagsMap
|
||||
|
||||
for k, v := range reservedTagsMap {
|
||||
if v {
|
||||
notNeedTagsMap = append(notNeedTagsMap, k)
|
||||
}
|
||||
}
|
||||
|
||||
return true, []string{}
|
||||
if len(needTagsMap) > 0 {
|
||||
return false, true, needTagsMap, []string{}
|
||||
}
|
||||
|
||||
if len(notNeedTagsMap) > 0 {
|
||||
return true, false, []string{}, notNeedTagsMap
|
||||
}
|
||||
|
||||
return true, true, []string{}, []string{}
|
||||
}
|
||||
|
||||
// ObjectChangeTag change object tag list
|
||||
|
|
|
@ -2,6 +2,7 @@ package converter
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
@ -25,6 +26,29 @@ func IntToString(data int64) string {
|
|||
return fmt.Sprintf("%d", data)
|
||||
}
|
||||
|
||||
// InterfaceToString converts data to string
|
||||
// It will be used in template render
|
||||
func InterfaceToString(data interface{}) string {
|
||||
return fmt.Sprintf("%d", data)
|
||||
switch t := data.(type) {
|
||||
case int:
|
||||
i := data.(int)
|
||||
return strconv.Itoa(i)
|
||||
case int8:
|
||||
i := data.(int8)
|
||||
return strconv.Itoa(int(i))
|
||||
case int16:
|
||||
i := data.(int16)
|
||||
return strconv.Itoa(int(i))
|
||||
case int32:
|
||||
i := data.(int32)
|
||||
return string(i)
|
||||
case int64:
|
||||
i := data.(int64)
|
||||
return strconv.FormatInt(i, 10)
|
||||
case string:
|
||||
return data.(string)
|
||||
default:
|
||||
log.Warn("can't convert type:", t)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
<!doctype html><html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Answer</title><script defer="defer" src="/static/js/main.2617abc6.js"></script><link href="/static/css/main.d4180d41.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><style>@keyframes _doc-spin{to{transform:rotate(360deg)}}#doc-spinner{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%)}#doc-spinner .spinner{box-sizing:border-box;display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25rem solid currentColor;border-right-color:transparent;color:rgba(108,117,125,.75);border-radius:50%;animation:.75s linear infinite _doc-spin}</style><div id="doc-spinner"><div class="spinner"></div></div></div></body></html>
|
||||
<!doctype html><html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Answer</title><script defer="defer" src="/static/js/main.9de9552b.js"></script><link href="/static/css/main.d4180d41.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><style>@keyframes _doc-spin{to{transform:rotate(360deg)}}#doc-spinner{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%)}#doc-spinner .spinner{box-sizing:border-box;display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25rem solid currentColor;border-right-color:transparent;color:rgba(108,117,125,.75);border-radius:50%;animation:.75s linear infinite _doc-spin}</style><div id="doc-spinner"><div class="spinner"></div></div></div></body></html>
|
|
@ -1,12 +1,11 @@
|
|||
{{define "footer"}}
|
||||
</div>
|
||||
|
||||
<footer class="bg-light py-3">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0 fs-14 text-secondary">
|
||||
Built on <a href="https://answer.dev/" target="_blank"> Answer </a>-
|
||||
the open-source software that power Q&A communities.<br />Made
|
||||
with love © {{.siteinfo.Year}} {{.siteinfo.General.Name}}.
|
||||
{{$cc := (join " " .siteinfo.Year .siteinfo.General.Name) -}}
|
||||
{{- $ft := translator $.language "ui.footer.build_on" "cc" $cc -}}
|
||||
{{templateHTML (replaceHTMLTag $ft "<a href=\"https://answer.dev/\" target=\"_blank\"> Answer </a>")}}
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{{template "header" . }}
|
||||
{{translator $.language "error.object.old_password_verification_failed"}}
|
||||
<div class="pt-4 mt-2 mb-5 container">
|
||||
<div class="justify-content-center row">
|
||||
<div class="col-xxl-7 col-lg-8 col-sm-12">
|
||||
|
|
|
@ -21,10 +21,17 @@
|
|||
class="fw-bold" title="Reputation">{{.UserInfo.Rank}}</span>
|
||||
</div>
|
||||
•
|
||||
{{if eq .CreateTime .UpdateTime}}
|
||||
<time class="text-secondary ms-1"
|
||||
datetime="{{timeFormatISO $.timezone .CreateTime}}"
|
||||
title="{{translatorTimeFormatLongDate $.language $.timezone .CreateTime}}">{{translator $.language "ui.question.asked"}} {{translatorTimeFormat $.language $.timezone .CreateTime}}
|
||||
</time>
|
||||
{{else if gt .UpdateTime 0}}
|
||||
<time class="text-secondary ms-1"
|
||||
datetime="{{timeFormatISO $.timezone .UpdateTime}}"
|
||||
title="{{translatorTimeFormatLongDate $.language $.timezone .UpdateTime}}">{{translator $.language "ui.question.modified"}} {{translatorTimeFormat $.language $.timezone .UpdateTime}}
|
||||
</time>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="ms-0 ms-md-3 mt-2 mt-md-0">
|
||||
<span><i class="br bi-hand-thumbs-up-fill"></i><em
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
</div>
|
||||
<div>
|
||||
<div class="mb-3 d-flex flex-wrap justify-content-between">
|
||||
<h5 class="fs-5 text-nowrap mb-3 mb-md-0">{{ .questionCount }}
|
||||
{{translator ($.language) ("ui.question.questions")}}</h5>
|
||||
<h5 class="fs-5 text-nowrap mb-3 mb-md-0">
|
||||
{{translator ($.language) "ui.question.x_questions" "count" .questionCount}}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="border-top border-bottom-0 list-group list-group-flush">
|
||||
{{range .questionList}}
|
||||
|
|
Loading…
Reference in New Issue