mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 08:19:15 +08:00
feat: 网站增加密码访问功能 (#782)
This commit is contained in:
parent
79622f324b
commit
37806f1113
@ -715,3 +715,46 @@ func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
helper.SuccessWithOutData(c)
|
helper.SuccessWithOutData(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Website
|
||||||
|
// @Summary Get AuthBasic conf
|
||||||
|
// @Description 获取密码访问配置
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.NginxAuthReq true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /websites/auths [post]
|
||||||
|
func (b *BaseApi) GetAuthConfig(c *gin.Context) {
|
||||||
|
var req request.NginxAuthReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := websiteService.GetAuthBasics(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Website
|
||||||
|
// @Summary Get AuthBasic conf
|
||||||
|
// @Description 更新密码访问配置
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.NginxAuthUpdate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /websites/auths/update [post]
|
||||||
|
func (b *BaseApi) UpdateAuthConfig(c *gin.Context) {
|
||||||
|
var req request.NginxAuthUpdate
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := websiteService.UpdateAuthBasic(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithOutData(c)
|
||||||
|
}
|
||||||
|
@ -28,6 +28,11 @@ type NginxParam struct {
|
|||||||
Params []string
|
Params []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NginxAuth struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
type NginxKey string
|
type NginxKey string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -36,3 +36,15 @@ type NginxProxyUpdate struct {
|
|||||||
Content string `json:"content" validate:"required"`
|
Content string `json:"content" validate:"required"`
|
||||||
Name string `json:"name" validate:"required"`
|
Name string `json:"name" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NginxAuthUpdate struct {
|
||||||
|
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||||
|
Operate string `json:"operate" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Password string `json:"password" validate:"required"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NginxAuthReq struct {
|
||||||
|
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||||
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package response
|
package response
|
||||||
|
|
||||||
|
import "github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
|
||||||
type NginxStatus struct {
|
type NginxStatus struct {
|
||||||
Active string `json:"active"`
|
Active string `json:"active"`
|
||||||
Accepts string `json:"accepts"`
|
Accepts string `json:"accepts"`
|
||||||
@ -14,3 +16,8 @@ type NginxParam struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Params []string `json:"params"`
|
Params []string `json:"params"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NginxAuthRes struct {
|
||||||
|
Enable bool `json:"enable"`
|
||||||
|
Items []dto.NginxAuth `json:"items"`
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
|
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
|
||||||
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
|
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@ -70,6 +71,8 @@ type IWebsiteService interface {
|
|||||||
OperateProxy(req request.WebsiteProxyConfig) (err error)
|
OperateProxy(req request.WebsiteProxyConfig) (err error)
|
||||||
GetProxies(id uint) (res []request.WebsiteProxyConfig, err error)
|
GetProxies(id uint) (res []request.WebsiteProxyConfig, err error)
|
||||||
UpdateProxyFile(req request.NginxProxyUpdate) (err error)
|
UpdateProxyFile(req request.NginxProxyUpdate) (err error)
|
||||||
|
GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error)
|
||||||
|
UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIWebsiteService() IWebsiteService {
|
func NewIWebsiteService() IWebsiteService {
|
||||||
@ -1327,3 +1330,170 @@ func (w WebsiteService) UpdateProxyFile(req request.NginxProxyUpdate) (err error
|
|||||||
}()
|
}()
|
||||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error) {
|
||||||
|
var (
|
||||||
|
website model.Website
|
||||||
|
nginxInstall model.AppInstall
|
||||||
|
params []dto.NginxParam
|
||||||
|
authContent []byte
|
||||||
|
authArray []string
|
||||||
|
)
|
||||||
|
website, err = websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authPath := fmt.Sprintf("/www/sites/%s/auth_basic/auth.pass", website.Alias)
|
||||||
|
absoluteAuthPath := path.Join(nginxInstall.GetPath(), authPath)
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
if !fileOp.Stat(path.Dir(absoluteAuthPath)) {
|
||||||
|
_ = fileOp.CreateDir(path.Dir(absoluteAuthPath), 755)
|
||||||
|
}
|
||||||
|
if !fileOp.Stat(absoluteAuthPath) {
|
||||||
|
_ = fileOp.CreateFile(absoluteAuthPath)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
switch req.Operate {
|
||||||
|
case "create":
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
params = append(params, dto.NginxParam{Name: "auth_basic", Params: []string{`"Authentication"`}})
|
||||||
|
params = append(params, dto.NginxParam{Name: "auth_basic_user_file", Params: []string{authPath}})
|
||||||
|
authContent, err = fileOp.GetContent(absoluteAuthPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authArray = strings.Split(string(authContent), "\n")
|
||||||
|
switch req.Operate {
|
||||||
|
case "disable":
|
||||||
|
return deleteNginxConfig(constant.NginxScopeServer, params, &website)
|
||||||
|
case "enable":
|
||||||
|
return updateNginxConfig(constant.NginxScopeServer, params, &website)
|
||||||
|
case "create":
|
||||||
|
for _, line := range authArray {
|
||||||
|
authParams := strings.Split(line, ":")
|
||||||
|
username := authParams[0]
|
||||||
|
if username == req.Username {
|
||||||
|
err = buserr.New(constant.ErrUsernameIsExist)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var passwdHash []byte
|
||||||
|
passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
line := fmt.Sprintf("%s:%s\n", req.Username, passwdHash)
|
||||||
|
if req.Remark != "" {
|
||||||
|
line = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark)
|
||||||
|
}
|
||||||
|
authArray = append(authArray, line)
|
||||||
|
case "edit":
|
||||||
|
userExist := false
|
||||||
|
for index, line := range authArray {
|
||||||
|
authParams := strings.Split(line, ":")
|
||||||
|
username := authParams[0]
|
||||||
|
if username == req.Username {
|
||||||
|
userExist = true
|
||||||
|
var passwdHash []byte
|
||||||
|
passwdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userPasswd := fmt.Sprintf("%s:%s\n", req.Username, passwdHash)
|
||||||
|
if req.Remark != "" {
|
||||||
|
userPasswd = fmt.Sprintf("%s:%s:%s\n", req.Username, passwdHash, req.Remark)
|
||||||
|
}
|
||||||
|
authArray[index] = userPasswd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !userExist {
|
||||||
|
err = buserr.New(constant.ErrUsernameIsNotExist)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case "delete":
|
||||||
|
deleteIndex := -1
|
||||||
|
for index, line := range authArray {
|
||||||
|
authParams := strings.Split(line, ":")
|
||||||
|
username := authParams[0]
|
||||||
|
if username == req.Username {
|
||||||
|
deleteIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if deleteIndex < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authArray = append(authArray[:deleteIndex], authArray[deleteIndex+1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var passFile *os.File
|
||||||
|
passFile, err = os.Create(absoluteAuthPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer passFile.Close()
|
||||||
|
writer := bufio.NewWriter(passFile)
|
||||||
|
for _, line := range authArray {
|
||||||
|
_, err = writer.WriteString(line + "\n")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = writer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) {
|
||||||
|
var (
|
||||||
|
website model.Website
|
||||||
|
nginxInstall model.AppInstall
|
||||||
|
authContent []byte
|
||||||
|
nginxParams []response.NginxParam
|
||||||
|
)
|
||||||
|
website, err = websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authPath := fmt.Sprintf("/www/sites/%s/auth_basic/auth.pass", website.Alias)
|
||||||
|
absoluteAuthPath := path.Join(nginxInstall.GetPath(), authPath)
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
if !fileOp.Stat(absoluteAuthPath) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nginxParams, err = getNginxParamsByKeys(constant.NginxScopeServer, []string{"auth_basic"}, &website)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.Enable = len(nginxParams[0].Params) > 0
|
||||||
|
authContent, err = fileOp.GetContent(absoluteAuthPath)
|
||||||
|
authArray := strings.Split(string(authContent), "\n")
|
||||||
|
for _, line := range authArray {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
params := strings.Split(line, ":")
|
||||||
|
auth := dto.NginxAuth{
|
||||||
|
Username: params[0],
|
||||||
|
}
|
||||||
|
if len(params) == 3 {
|
||||||
|
auth.Remark = params[2]
|
||||||
|
}
|
||||||
|
res.Items = append(res.Items, auth)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -65,10 +65,12 @@ var (
|
|||||||
|
|
||||||
// website
|
// website
|
||||||
var (
|
var (
|
||||||
ErrDomainIsExist = "ErrDomainIsExist"
|
ErrDomainIsExist = "ErrDomainIsExist"
|
||||||
ErrAliasIsExist = "ErrAliasIsExist"
|
ErrAliasIsExist = "ErrAliasIsExist"
|
||||||
ErrAppDelete = "ErrAppDelete"
|
ErrAppDelete = "ErrAppDelete"
|
||||||
ErrGroupIsUsed = "ErrGroupIsUsed"
|
ErrGroupIsUsed = "ErrGroupIsUsed"
|
||||||
|
ErrUsernameIsExist = "ErrUsernameIsExist"
|
||||||
|
ErrUsernameIsNotExist = "ErrUsernameIsNotExist"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ssl
|
// ssl
|
||||||
|
@ -43,6 +43,8 @@ ErrDomainIsExist: "Domain is already exist"
|
|||||||
ErrAliasIsExist: "Alias is already exist"
|
ErrAliasIsExist: "Alias is already exist"
|
||||||
ErrAppDelete: 'Other Website use this App'
|
ErrAppDelete: 'Other Website use this App'
|
||||||
ErrGroupIsUsed: 'The group is in use and cannot be deleted'
|
ErrGroupIsUsed: 'The group is in use and cannot be deleted'
|
||||||
|
ErrUsernameIsExist: 'Username is already exist'
|
||||||
|
ErrUsernameIsNotExist: 'Username is not exist'
|
||||||
|
|
||||||
#ssl
|
#ssl
|
||||||
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
|
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
|
||||||
|
@ -43,6 +43,8 @@ ErrDomainIsExist: "域名已存在"
|
|||||||
ErrAliasIsExist: "代号已存在"
|
ErrAliasIsExist: "代号已存在"
|
||||||
ErrAppDelete: '其他网站使用此应用,无法删除'
|
ErrAppDelete: '其他网站使用此应用,无法删除'
|
||||||
ErrGroupIsUsed: '分组正在使用中,无法删除'
|
ErrGroupIsUsed: '分组正在使用中,无法删除'
|
||||||
|
ErrUsernameIsExist: '用户名已存在'
|
||||||
|
ErrUsernameIsNotExist: '用户不存在'
|
||||||
|
|
||||||
#ssl
|
#ssl
|
||||||
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
|
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
|
||||||
|
@ -55,5 +55,8 @@ func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) {
|
|||||||
groupRouter.POST("/proxies", baseApi.GetProxyConfig)
|
groupRouter.POST("/proxies", baseApi.GetProxyConfig)
|
||||||
groupRouter.POST("/proxies/update", baseApi.UpdateProxyConfig)
|
groupRouter.POST("/proxies/update", baseApi.UpdateProxyConfig)
|
||||||
groupRouter.POST("/proxies/file", baseApi.UpdateProxyConfigFile)
|
groupRouter.POST("/proxies/file", baseApi.UpdateProxyConfigFile)
|
||||||
|
|
||||||
|
groupRouter.POST("/auths", baseApi.GetAuthConfig)
|
||||||
|
groupRouter.POST("/auths/update", baseApi.UpdateAuthConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7904,6 +7904,72 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/websites/auths": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取密码访问配置",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Website"
|
||||||
|
],
|
||||||
|
"summary": "Get AuthBasic conf",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.NginxAuthReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/websites/auths/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新密码访问配置",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Website"
|
||||||
|
],
|
||||||
|
"summary": "Get AuthBasic conf",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.NginxAuthUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/websites/check": {
|
"/websites/check": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -12764,6 +12830,43 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.NginxAuthReq": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"websiteID"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"websiteID": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request.NginxAuthUpdate": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"operate",
|
||||||
|
"password",
|
||||||
|
"username",
|
||||||
|
"websiteID"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"operate": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"remark": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"websiteID": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.NginxConfigFileUpdate": {
|
"request.NginxConfigFileUpdate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -7897,6 +7897,72 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/websites/auths": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取密码访问配置",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Website"
|
||||||
|
],
|
||||||
|
"summary": "Get AuthBasic conf",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.NginxAuthReq"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/websites/auths/update": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "更新密码访问配置",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Website"
|
||||||
|
],
|
||||||
|
"summary": "Get AuthBasic conf",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.NginxAuthUpdate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/websites/check": {
|
"/websites/check": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -12757,6 +12823,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.NginxAuthReq": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"websiteID"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"websiteID": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request.NginxAuthUpdate": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"operate",
|
||||||
|
"password",
|
||||||
|
"username",
|
||||||
|
"websiteID"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"operate": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"remark": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"websiteID": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.NginxConfigFileUpdate": {
|
"request.NginxConfigFileUpdate": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -2124,6 +2124,31 @@ definitions:
|
|||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
|
request.NginxAuthReq:
|
||||||
|
properties:
|
||||||
|
websiteID:
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- websiteID
|
||||||
|
type: object
|
||||||
|
request.NginxAuthUpdate:
|
||||||
|
properties:
|
||||||
|
operate:
|
||||||
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
remark:
|
||||||
|
type: string
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
websiteID:
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- operate
|
||||||
|
- password
|
||||||
|
- username
|
||||||
|
- websiteID
|
||||||
|
type: object
|
||||||
request.NginxConfigFileUpdate:
|
request.NginxConfigFileUpdate:
|
||||||
properties:
|
properties:
|
||||||
backup:
|
backup:
|
||||||
@ -8018,6 +8043,46 @@ paths:
|
|||||||
summary: Page website acme accounts
|
summary: Page website acme accounts
|
||||||
tags:
|
tags:
|
||||||
- Website Acme
|
- Website Acme
|
||||||
|
/websites/auths:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取密码访问配置
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.NginxAuthReq'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Get AuthBasic conf
|
||||||
|
tags:
|
||||||
|
- Website
|
||||||
|
/websites/auths/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 更新密码访问配置
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.NginxAuthUpdate'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Get AuthBasic conf
|
||||||
|
tags:
|
||||||
|
- Website
|
||||||
/websites/check:
|
/websites/check:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
@ -338,4 +338,26 @@ export namespace Website {
|
|||||||
name: string;
|
name: string;
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AuthReq {
|
||||||
|
websiteID: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NginxAuth {
|
||||||
|
username: string;
|
||||||
|
remark: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthConfig {
|
||||||
|
enable: boolean;
|
||||||
|
items: NginxAuth[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NginxAuthConfig {
|
||||||
|
websiteID: number;
|
||||||
|
operate: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
remark: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,3 +198,11 @@ export const OperateProxyConfig = (req: Website.ProxyReq) => {
|
|||||||
export const UpdateProxyConfigFile = (req: Website.ProxyFileUpdate) => {
|
export const UpdateProxyConfigFile = (req: Website.ProxyFileUpdate) => {
|
||||||
return http.post<any>(`/websites/proxies/file`, req);
|
return http.post<any>(`/websites/proxies/file`, req);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const GetAuthConfig = (req: Website.AuthReq) => {
|
||||||
|
return http.post<Website.AuthConfig>(`/websites/auths`, req);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OperateAuthConfig = (req: Website.NginxAuthConfig) => {
|
||||||
|
return http.post<any>(`/websites/auths/update`, req);
|
||||||
|
};
|
||||||
|
@ -1224,6 +1224,10 @@ const message = {
|
|||||||
replaceText: 'Replacement text, can be empty',
|
replaceText: 'Replacement text, can be empty',
|
||||||
replacedErr: 'The replaced text cannot be empty',
|
replacedErr: 'The replaced text cannot be empty',
|
||||||
replacedErr2: 'The replaced text cannot be repeated',
|
replacedErr2: 'The replaced text cannot be repeated',
|
||||||
|
basicAuth: 'Password Access',
|
||||||
|
editBasicAuthHelper:
|
||||||
|
'The password is asymmetrically encrypted and cannot be echoed. Editing needs to reset the password',
|
||||||
|
createPassword: 'Generate password',
|
||||||
},
|
},
|
||||||
php: {
|
php: {
|
||||||
short_open_tag: 'Short tag support',
|
short_open_tag: 'Short tag support',
|
||||||
|
@ -1223,6 +1223,9 @@ const message = {
|
|||||||
replaceText: '替换的文本,可为空',
|
replaceText: '替换的文本,可为空',
|
||||||
replacedErr: '被替换的文本不能为空',
|
replacedErr: '被替换的文本不能为空',
|
||||||
replacedErr2: '被替换的文本不能重复',
|
replacedErr2: '被替换的文本不能重复',
|
||||||
|
basicAuth: '密码访问',
|
||||||
|
editBasicAuthHelper: '密码为非对称加密,无法回显,编辑需要重新设置密码',
|
||||||
|
createPassword: '生成密码',
|
||||||
},
|
},
|
||||||
php: {
|
php: {
|
||||||
short_open_tag: '短标签支持',
|
short_open_tag: '短标签支持',
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer v-model="open" :close-on-click-modal="false" size="40%" :before-close="handleClose">
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader
|
||||||
|
:header="$t('commons.button.' + authBasic.operate) + $t('website.basicAuth')"
|
||||||
|
:back="handleClose"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<el-row v-loading="loading">
|
||||||
|
<el-col :span="22" :offset="1">
|
||||||
|
<el-form-item>
|
||||||
|
<el-alert
|
||||||
|
v-if="authBasic.operate === 'edit'"
|
||||||
|
:title="$t('website.editBasicAuthHelper')"
|
||||||
|
type="info"
|
||||||
|
:closable="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form ref="proxyForm" label-position="top" :model="authBasic" :rules="rules">
|
||||||
|
<el-form-item :label="$t('commons.table.name')" prop="username">
|
||||||
|
<el-input v-model.trim="authBasic.username" :disabled="authBasic.operate === 'edit'"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.login.password')" prop="password">
|
||||||
|
<el-input type="password" clearable show-password v-model.trim="authBasic.password">
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="random">
|
||||||
|
{{ $t('website.createPassword') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('website.remark')" prop="remark">
|
||||||
|
<el-input v-model.trim="authBasic.remark"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="submit(proxyForm)" :disabled="loading">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
|
import { OperateAuthConfig } from '@/api/modules/website';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { FormInstance } from 'element-plus';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { Website } from '@/api/interface/website';
|
||||||
|
import { getRandomStr } from '@/utils/util';
|
||||||
|
|
||||||
|
const proxyForm = ref<FormInstance>();
|
||||||
|
const rules = ref({
|
||||||
|
username: [Rules.requiredInput, Rules.name],
|
||||||
|
password: [Rules.requiredInput],
|
||||||
|
});
|
||||||
|
const open = ref(false);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const initData = (): Website.NginxAuthConfig => ({
|
||||||
|
websiteID: 0,
|
||||||
|
operate: 'create',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
remark: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
let authBasic = ref(initData());
|
||||||
|
const em = defineEmits(['close']);
|
||||||
|
const handleClose = () => {
|
||||||
|
proxyForm.value?.resetFields();
|
||||||
|
open.value = false;
|
||||||
|
em('close', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const random = async () => {
|
||||||
|
authBasic.value.password = getRandomStr(16);
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptParams = (proxyParam: Website.NginxAuthConfig) => {
|
||||||
|
authBasic.value = proxyParam;
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
await formEl.validate((valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loading.value = true;
|
||||||
|
OperateAuthConfig(authBasic.value)
|
||||||
|
.then(() => {
|
||||||
|
if (authBasic.value.operate == 'create') {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||||
|
} else {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||||
|
}
|
||||||
|
handleClose();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<el-form-item prop="enable" :label="$t('website.enableOrNot')">
|
||||||
|
<el-switch v-model="enable" @change="changeEnable"></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<ComplexTable :data="data" @search="search" v-loading="loading">
|
||||||
|
<template #toolbar>
|
||||||
|
<el-button type="primary" plain @click="openCreate">
|
||||||
|
{{ $t('commons.button.create') + $t('website.basicAuth') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<el-table-column :label="$t('commons.table.name')" prop="username"></el-table-column>
|
||||||
|
<el-table-column :label="$t('website.remark')" prop="remark"></el-table-column>
|
||||||
|
<fu-table-operations
|
||||||
|
:ellipsis="10"
|
||||||
|
width="260px"
|
||||||
|
:buttons="buttons"
|
||||||
|
:label="$t('commons.table.operate')"
|
||||||
|
fixed="right"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
<Create ref="createRef" @close="search()" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="proxy">
|
||||||
|
import { Website } from '@/api/interface/website';
|
||||||
|
import { OperateAuthConfig, GetAuthConfig } from '@/api/modules/website';
|
||||||
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import Create from './create/index.vue';
|
||||||
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const id = computed(() => {
|
||||||
|
return props.id;
|
||||||
|
});
|
||||||
|
const loading = ref(false);
|
||||||
|
const data = ref();
|
||||||
|
const createRef = ref();
|
||||||
|
const enable = ref(false);
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.edit'),
|
||||||
|
click: function (row: Website.NginxAuthConfig) {
|
||||||
|
openEdit(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
click: function (row: Website.NginxAuthConfig) {
|
||||||
|
deleteAuth(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const initData = (id: number): Website.NginxAuthConfig => ({
|
||||||
|
websiteID: id,
|
||||||
|
operate: 'create',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
remark: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const openCreate = () => {
|
||||||
|
createRef.value.acceptParams(initData(id.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const openEdit = (authConfig: Website.NginxAuthConfig) => {
|
||||||
|
let authParam = JSON.parse(JSON.stringify(authConfig));
|
||||||
|
authParam.operate = 'edit';
|
||||||
|
authParam.websiteID = id.value;
|
||||||
|
createRef.value.acceptParams(authParam);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteAuth = async (authConfig: Website.NginxAuthConfig) => {
|
||||||
|
authConfig.operate = 'delete';
|
||||||
|
authConfig.websiteID = id.value;
|
||||||
|
await useDeleteData(OperateAuthConfig, authConfig, 'commons.msg.delete');
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeEnable = () => {
|
||||||
|
const req = initData(id.value);
|
||||||
|
req.operate = enable.value ? 'enable' : 'disable';
|
||||||
|
loading.value = true;
|
||||||
|
OperateAuthConfig(req)
|
||||||
|
.then(() => {
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||||
|
search();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const res = await GetAuthConfig({ websiteID: id.value });
|
||||||
|
data.value = res.data.items || [];
|
||||||
|
enable.value = res.data.enable;
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
@ -15,14 +15,17 @@
|
|||||||
<el-tab-pane :label="$t('website.proxy')">
|
<el-tab-pane :label="$t('website.proxy')">
|
||||||
<Proxy :id="id" v-if="tabIndex == '4'"></Proxy>
|
<Proxy :id="id" v-if="tabIndex == '4'"></Proxy>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
<el-tab-pane :label="$t('website.basicAuth')">
|
||||||
|
<AuthBasic :id="id" v-if="tabIndex == '5'"></AuthBasic>
|
||||||
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="'HTTPS'">
|
<el-tab-pane :label="'HTTPS'">
|
||||||
<HTTPS :id="id" v-if="tabIndex == '5'"></HTTPS>
|
<HTTPS :id="id" v-if="tabIndex == '6'"></HTTPS>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="$t('website.rewrite')">
|
<el-tab-pane :label="$t('website.rewrite')">
|
||||||
<Rewrite :id="id" v-if="tabIndex == '6'"></Rewrite>
|
<Rewrite :id="id" v-if="tabIndex == '7'"></Rewrite>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="$t('website.other')">
|
<el-tab-pane :label="$t('website.other')">
|
||||||
<Other :id="id" v-if="tabIndex == '7'"></Other>
|
<Other :id="id" v-if="tabIndex == '8'"></Other>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</template>
|
</template>
|
||||||
@ -38,6 +41,7 @@ import HTTPS from './https/index.vue';
|
|||||||
import SitePath from './site-folder/index.vue';
|
import SitePath from './site-folder/index.vue';
|
||||||
import Rewrite from './rewrite/index.vue';
|
import Rewrite from './rewrite/index.vue';
|
||||||
import Proxy from './proxy/index.vue';
|
import Proxy from './proxy/index.vue';
|
||||||
|
import AuthBasic from './auth-basic/index.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user