mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-23 18:49:21 +08:00
feat: Add API interface authentication function (#7146)
This commit is contained in:
parent
c2fd02ac48
commit
28597721f2
@ -342,3 +342,52 @@ func (b *BaseApi) MFABind(c *gin.Context) {
|
||||
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary generate api key
|
||||
// @Description 生成 API 接口密钥
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/api/config/generate/key [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 API 接口密钥","formatEN":"generate api key"}
|
||||
func (b *BaseApi) GenerateApiKey(c *gin.Context) {
|
||||
panelToken := c.GetHeader("1Panel-Token")
|
||||
if panelToken != "" {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigDisable, nil)
|
||||
return
|
||||
}
|
||||
apiKey, err := settingService.GenerateApiKey()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, apiKey)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update api config
|
||||
// @Description 更新 API 接口配置
|
||||
// @Accept json
|
||||
// @Param request body dto.ApiInterfaceConfig true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/api/config/update [post]
|
||||
// @x-panel-log {"bodyKeys":["ipWhiteList"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 API 接口配置 => IP 白名单: [ipWhiteList]","formatEN":"update api config => IP White List: [ipWhiteList]"}
|
||||
func (b *BaseApi) UpdateApiConfig(c *gin.Context) {
|
||||
panelToken := c.GetHeader("1Panel-Token")
|
||||
if panelToken != "" {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigDisable, nil)
|
||||
return
|
||||
}
|
||||
var req dto.ApiInterfaceConfig
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.UpdateApiConfig(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
@ -66,6 +66,10 @@ type SettingInfo struct {
|
||||
ProxyUser string `json:"proxyUser"`
|
||||
ProxyPasswd string `json:"proxyPasswd"`
|
||||
ProxyPasswdKeep string `json:"proxyPasswdKeep"`
|
||||
|
||||
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
IpWhiteList string `json:"ipWhiteList"`
|
||||
}
|
||||
|
||||
type SettingUpdate struct {
|
||||
@ -231,3 +235,9 @@ type XpackHideMenu struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Children []XpackHideMenu `json:"children,omitempty"`
|
||||
}
|
||||
|
||||
type ApiInterfaceConfig struct {
|
||||
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
IpWhiteList string `json:"ipWhiteList"`
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ type ISettingService interface {
|
||||
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
|
||||
LoadFromCert() (*dto.SSLInfo, error)
|
||||
HandlePasswordExpired(c *gin.Context, old, new string) error
|
||||
GenerateApiKey() (string, error)
|
||||
UpdateApiConfig(req dto.ApiInterfaceConfig) error
|
||||
}
|
||||
|
||||
func NewISettingService() ISettingService {
|
||||
@ -485,3 +487,28 @@ func checkCertValid() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) GenerateApiKey() (string, error) {
|
||||
apiKey := common.RandStr(32)
|
||||
if err := settingRepo.Update("ApiKey", apiKey); err != nil {
|
||||
return global.CONF.System.ApiKey, err
|
||||
}
|
||||
global.CONF.System.ApiKey = apiKey
|
||||
return apiKey, nil
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdateApiConfig(req dto.ApiInterfaceConfig) error {
|
||||
if err := settingRepo.Update("ApiInterfaceStatus", req.ApiInterfaceStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
global.CONF.System.ApiInterfaceStatus = req.ApiInterfaceStatus
|
||||
if err := settingRepo.Update("ApiKey", req.ApiKey); err != nil {
|
||||
return err
|
||||
}
|
||||
global.CONF.System.ApiKey = req.ApiKey
|
||||
if err := settingRepo.Update("IpWhiteList", req.IpWhiteList); err != nil {
|
||||
return err
|
||||
}
|
||||
global.CONF.System.IpWhiteList = req.IpWhiteList
|
||||
return nil
|
||||
}
|
||||
|
@ -25,4 +25,7 @@ type System struct {
|
||||
ChangeUserInfo string `mapstructure:"change_user_info"`
|
||||
OneDriveID string `mapstructure:"one_drive_id"`
|
||||
OneDriveSc string `mapstructure:"one_drive_sc"`
|
||||
ApiInterfaceStatus string `mapstructure:"api_interface_status"`
|
||||
ApiKey string `mapstructure:"api_key"`
|
||||
IpWhiteList string `mapstructure:"ip_white_list"`
|
||||
}
|
||||
|
@ -49,6 +49,10 @@ var (
|
||||
ErrXpackLost = "ErrXpackLost"
|
||||
ErrXpackTimeout = "ErrXpackTimeout"
|
||||
ErrXpackOutOfDate = "ErrXpackOutOfDate"
|
||||
ErrApiConfigStatusInvalid = "ErrApiConfigStatusInvalid"
|
||||
ErrApiConfigKeyInvalid = "ErrApiConfigKeyInvalid"
|
||||
ErrApiConfigIPInvalid = "ErrApiConfigIPInvalid"
|
||||
ErrApiConfigDisable = "ErrApiConfigDisable"
|
||||
)
|
||||
|
||||
// app
|
||||
|
@ -8,6 +8,10 @@ ErrStructTransform: "Type conversion failure: {{ .detail }}"
|
||||
ErrNotLogin: "User is not Login: {{ .detail }}"
|
||||
ErrPasswordExpired: "The current password has expired: {{ .detail }}"
|
||||
ErrNotSupportType: "The system does not support the current type: {{ .detail }}"
|
||||
ErrApiConfigStatusInvalid: "API Interface access prohibited: {{ .detail }}"
|
||||
ErrApiConfigKeyInvalid: "API Interface key error: {{ .detail }}"
|
||||
ErrApiConfigIPInvalid: "API Interface IP is not on the whitelist: {{ .detail }}"
|
||||
ErrApiConfigDisable: "This interface prohibits the use of API Interface calls: {{ .detail }}"
|
||||
|
||||
#common
|
||||
ErrNameIsExist: "Name is already exist"
|
||||
|
@ -8,6 +8,10 @@ ErrStructTransform: "類型轉換失敗: {{ .detail }}"
|
||||
ErrNotLogin: "用戶未登入: {{ .detail }}"
|
||||
ErrPasswordExpired: "當前密碼已過期: {{ .detail }}"
|
||||
ErrNotSupportType: "系統暫不支持當前類型: {{ .detail }}"
|
||||
ErrApiConfigStatusInvalid: "API 接口禁止訪問: {{ .detail }}"
|
||||
ErrApiConfigKeyInvalid: "API 接口密钥錯誤: {{ .detail }}"
|
||||
ErrApiConfigIPInvalid: "调用 API 接口 IP 不在白名单: {{ .detail }}"
|
||||
ErrApiConfigDisable: "此接口禁止使用 API 接口調用: {{ .detail }}"
|
||||
|
||||
#common
|
||||
ErrNameIsExist: "名稱已存在"
|
||||
|
@ -8,6 +8,10 @@ ErrStructTransform: "类型转换失败: {{ .detail }}"
|
||||
ErrNotLogin: "用户未登录: {{ .detail }}"
|
||||
ErrPasswordExpired: "当前密码已过期: {{ .detail }}"
|
||||
ErrNotSupportType: "系统暂不支持当前类型: {{ .detail }}"
|
||||
ErrApiConfigStatusInvalid: "API 接口禁止访问: {{ .detail }}"
|
||||
ErrApiConfigKeyInvalid: "API 接口密钥错误: {{ .detail }}"
|
||||
ErrApiConfigIPInvalid: "调用 API 接口 IP 不在白名单: {{ .detail }}"
|
||||
ErrApiConfigDisable: "此接口禁止使用 API 接口调用: {{ .detail }}"
|
||||
|
||||
#common
|
||||
ErrNameIsExist: "名称已存在"
|
||||
|
@ -61,6 +61,24 @@ func Init() {
|
||||
global.LOG.Fatalf("init service before start failed, err: %v", err)
|
||||
}
|
||||
|
||||
apiInterfaceStatusSetting, err := settingRepo.Get(settingRepo.WithByKey("ApiInterfaceStatus"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load service api interface from setting failed, err: %v", err)
|
||||
}
|
||||
global.CONF.System.ApiInterfaceStatus = apiInterfaceStatusSetting.Value
|
||||
if apiInterfaceStatusSetting.Value == "enable" {
|
||||
apiKeySetting, err := settingRepo.Get(settingRepo.WithByKey("ApiKey"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load service api key from setting failed, err: %v", err)
|
||||
}
|
||||
global.CONF.System.ApiKey = apiKeySetting.Value
|
||||
ipWhiteListSetting, err := settingRepo.Get(settingRepo.WithByKey("IpWhiteList"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load service ip white list from setting failed, err: %v", err)
|
||||
}
|
||||
global.CONF.System.IpWhiteList = ipWhiteListSetting.Value
|
||||
}
|
||||
|
||||
handleUserInfo(global.CONF.System.ChangeUserInfo, settingRepo)
|
||||
|
||||
handleCronjobStatus()
|
||||
|
@ -97,6 +97,7 @@ func Init() {
|
||||
migrations.AddComposeColumn,
|
||||
|
||||
migrations.AddAutoRestart,
|
||||
migrations.AddApiInterfaceConfig,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
@ -334,3 +334,19 @@ var AddAutoRestart = &gormigrate.Migration{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddApiInterfaceConfig = &gormigrate.Migration{
|
||||
ID: "202411-add-api-interface-config",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Create(&model.Setting{Key: "ApiInterfaceStatus", Value: "disable"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "ApiKey", Value: ""}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "IpWhiteList", Value: ""}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
@ -16,6 +20,28 @@ func SessionAuth() gin.HandlerFunc {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
panelToken := c.GetHeader("1Panel-Token")
|
||||
panelTimestamp := c.GetHeader("1Panel-Timestamp")
|
||||
if panelToken != "" || panelTimestamp != "" {
|
||||
if global.CONF.System.ApiInterfaceStatus == "enable" {
|
||||
clientIP := c.ClientIP()
|
||||
if !isValid1PanelToken(panelToken, panelTimestamp) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigKeyInvalid, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !isIPInWhiteList(clientIP) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigIPInvalid, nil)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
return
|
||||
} else {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrApiConfigStatusInvalid, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
sId, err := c.Cookie(constant.SessionName)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrTypeNotLogin, nil)
|
||||
@ -36,3 +62,38 @@ func SessionAuth() gin.HandlerFunc {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func isValid1PanelToken(panelToken string, panelTimestamp string) bool {
|
||||
system1PanelToken := global.CONF.System.ApiKey
|
||||
if GenerateMD5("1panel"+panelToken+panelTimestamp) == GenerateMD5("1panel"+system1PanelToken+panelTimestamp) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isIPInWhiteList(clientIP string) bool {
|
||||
ipWhiteString := global.CONF.System.IpWhiteList
|
||||
ipWhiteList := strings.Split(ipWhiteString, "\n")
|
||||
for _, cidr := range ipWhiteList {
|
||||
if cidr == "0.0.0.0" {
|
||||
return true
|
||||
}
|
||||
_, ipNet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
if cidr == clientIP {
|
||||
return true
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ipNet.Contains(net.ParseIP(clientIP)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GenerateMD5(input string) string {
|
||||
hash := md5.New()
|
||||
hash.Write([]byte(input))
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
@ -64,5 +64,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
settingRouter.POST("/upgrade/notes", baseApi.GetNotesByVersion)
|
||||
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
|
||||
settingRouter.GET("/basedir", baseApi.LoadBaseDir)
|
||||
settingRouter.POST("/api/config/generate/key", baseApi.GenerateApiKey)
|
||||
settingRouter.POST("/api/config/update", baseApi.UpdateApiConfig)
|
||||
}
|
||||
}
|
||||
|
@ -887,20 +887,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/issafety": {
|
||||
"get": {
|
||||
"description": "获取系统安全登录状态",
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Load safety status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/language": {
|
||||
"get": {
|
||||
"description": "获取系统语言设置",
|
||||
@ -9507,6 +9493,77 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/api/config/generate/key": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "生成 API 接口密钥",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "generate api key",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [],
|
||||
"formatEN": "generate api key",
|
||||
"formatZH": "生成 API 接口密钥",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/api/config/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新 API 接口配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Update api config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ApiInterfaceConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"ipWhiteList"
|
||||
],
|
||||
"formatEN": "update api config =\u003e IP White List: [ipWhiteList]",
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/backup": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -15310,6 +15367,20 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ApiInterfaceConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apiInterfaceStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipWhiteList": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.AppInstallInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -19629,6 +19700,12 @@ const docTemplate = `{
|
||||
"allowIPs": {
|
||||
"type": "string"
|
||||
},
|
||||
"apiInterfaceStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"appStoreLastModified": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -19677,6 +19754,9 @@ const docTemplate = `{
|
||||
"fileRecycleBin": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipWhiteList": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipv6": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -23635,15 +23715,29 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"CustomToken": {
|
||||
"description": "自定义 Token 格式,格式:md5('1panel' + 1Panel-Token + 1Panel-Timestamp)。\n` + "`" + `` + "`" + `` + "`" + `\n示例请求头:\ncurl -X GET \"http://localhost:4004/api/v1/resource\" \\\n-H \"1Panel-Token: \u003c1panel_token\u003e\" \\\n-H \"1Panel-Timestamp: \u003ccurrent_unix_timestamp\u003e\"\n` + "`" + `` + "`" + `` + "`" + `\n- ` + "`" + `1Panel-Token` + "`" + ` 为面板 API 接口密钥。",
|
||||
"type": "apiKey",
|
||||
"name": "1Panel-Token",
|
||||
"in": "Header"
|
||||
},
|
||||
"Timestamp": {
|
||||
"description": "- ` + "`" + `1Panel-Timestamp` + "`" + ` 为当前时间的 Unix 时间戳(单位:秒)。",
|
||||
"type": "apiKey",
|
||||
"name": "1Panel-Timestamp",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "localhost",
|
||||
Host: "",
|
||||
BasePath: "/api/v1",
|
||||
Schemes: []string{},
|
||||
Schemes: []string{"http", "https"},
|
||||
Title: "1Panel",
|
||||
Description: "开源Linux面板",
|
||||
InfoInstanceName: "swagger",
|
||||
|
@ -1,4 +1,8 @@
|
||||
{
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "开源Linux面板",
|
||||
@ -11,7 +15,6 @@
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "localhost",
|
||||
"basePath": "/api/v1",
|
||||
"paths": {
|
||||
"/apps/:key": {
|
||||
@ -881,20 +884,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/issafety": {
|
||||
"get": {
|
||||
"description": "获取系统安全登录状态",
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Load safety status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/language": {
|
||||
"get": {
|
||||
"description": "获取系统语言设置",
|
||||
@ -9501,6 +9490,77 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/api/config/generate/key": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "生成 API 接口密钥",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "generate api key",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [],
|
||||
"formatEN": "generate api key",
|
||||
"formatZH": "生成 API 接口密钥",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/api/config/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新 API 接口配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Update api config",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ApiInterfaceConfig"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"ipWhiteList"
|
||||
],
|
||||
"formatEN": "update api config =\u003e IP White List: [ipWhiteList]",
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/settings/backup": {
|
||||
"post": {
|
||||
"security": [
|
||||
@ -15304,6 +15364,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ApiInterfaceConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apiInterfaceStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipWhiteList": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.AppInstallInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -19623,6 +19697,12 @@
|
||||
"allowIPs": {
|
||||
"type": "string"
|
||||
},
|
||||
"apiInterfaceStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"appStoreLastModified": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -19671,6 +19751,9 @@
|
||||
"fileRecycleBin": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipWhiteList": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipv6": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -23629,5 +23712,19 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"CustomToken": {
|
||||
"description": "自定义 Token 格式,格式:md5('1panel' + 1Panel-Token + 1Panel-Timestamp)。\n```\n示例请求头:\ncurl -X GET \"http://localhost:4004/api/v1/resource\" \\\n-H \"1Panel-Token: \u003c1panel_token\u003e\" \\\n-H \"1Panel-Timestamp: \u003ccurrent_unix_timestamp\u003e\"\n```\n- `1Panel-Token` 为面板 API 接口密钥。",
|
||||
"type": "apiKey",
|
||||
"name": "1Panel-Token",
|
||||
"in": "Header"
|
||||
},
|
||||
"Timestamp": {
|
||||
"description": "- `1Panel-Timestamp` 为当前时间的 Unix 时间戳(单位:秒)。",
|
||||
"type": "apiKey",
|
||||
"name": "1Panel-Timestamp",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,15 @@ definitions:
|
||||
oldRule:
|
||||
$ref: '#/definitions/dto.AddrRuleOperate'
|
||||
type: object
|
||||
dto.ApiInterfaceConfig:
|
||||
properties:
|
||||
apiInterfaceStatus:
|
||||
type: string
|
||||
apiKey:
|
||||
type: string
|
||||
ipWhiteList:
|
||||
type: string
|
||||
type: object
|
||||
dto.AppInstallInfo:
|
||||
properties:
|
||||
id:
|
||||
@ -2951,6 +2960,10 @@ definitions:
|
||||
properties:
|
||||
allowIPs:
|
||||
type: string
|
||||
apiInterfaceStatus:
|
||||
type: string
|
||||
apiKey:
|
||||
type: string
|
||||
appStoreLastModified:
|
||||
type: string
|
||||
appStoreSyncStatus:
|
||||
@ -2983,6 +2996,8 @@ definitions:
|
||||
type: string
|
||||
fileRecycleBin:
|
||||
type: string
|
||||
ipWhiteList:
|
||||
type: string
|
||||
ipv6:
|
||||
type: string
|
||||
language:
|
||||
@ -5624,7 +5639,6 @@ definitions:
|
||||
version:
|
||||
type: string
|
||||
type: object
|
||||
host: localhost
|
||||
info:
|
||||
contact: {}
|
||||
description: 开源Linux面板
|
||||
@ -6181,15 +6195,6 @@ paths:
|
||||
summary: Check System isDemo
|
||||
tags:
|
||||
- Auth
|
||||
/auth/issafety:
|
||||
get:
|
||||
description: 获取系统安全登录状态
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
summary: Load safety status
|
||||
tags:
|
||||
- Auth
|
||||
/auth/language:
|
||||
get:
|
||||
description: 获取系统语言设置
|
||||
@ -11638,6 +11643,52 @@ paths:
|
||||
formatEN: Update runtime [name]
|
||||
formatZH: 更新运行环境 [name]
|
||||
paramKeys: []
|
||||
/settings/api/config/generate/key:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 生成 API 接口密钥
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: generate api key
|
||||
tags:
|
||||
- System Setting
|
||||
x-panel-log:
|
||||
BeforeFunctions: []
|
||||
bodyKeys: []
|
||||
formatEN: generate api key
|
||||
formatZH: 生成 API 接口密钥
|
||||
paramKeys: []
|
||||
/settings/api/config/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新 API 接口配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.ApiInterfaceConfig'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update api config
|
||||
tags:
|
||||
- System Setting
|
||||
x-panel-log:
|
||||
BeforeFunctions: []
|
||||
bodyKeys:
|
||||
- ipWhiteList
|
||||
formatEN: 'update api config => IP White List: [ipWhiteList]'
|
||||
formatZH: '更新 API 接口配置 => IP 白名单: [ipWhiteList]'
|
||||
paramKeys: []
|
||||
/settings/backup:
|
||||
post:
|
||||
consumes:
|
||||
@ -15292,4 +15343,26 @@ paths:
|
||||
formatEN: Update website [primaryDomain]
|
||||
formatZH: 更新网站 [primaryDomain]
|
||||
paramKeys: []
|
||||
schemes:
|
||||
- http
|
||||
- https
|
||||
securityDefinitions:
|
||||
CustomToken:
|
||||
description: |-
|
||||
自定义 Token 格式,格式:md5('1panel' + 1Panel-Token + 1Panel-Timestamp)。
|
||||
```
|
||||
示例请求头:
|
||||
curl -X GET "http://localhost:4004/api/v1/resource" \
|
||||
-H "1Panel-Token: <1panel_token>" \
|
||||
-H "1Panel-Timestamp: <current_unix_timestamp>"
|
||||
```
|
||||
- `1Panel-Token` 为面板 API 接口密钥。
|
||||
in: Header
|
||||
name: 1Panel-Token
|
||||
type: apiKey
|
||||
Timestamp:
|
||||
description: '- `1Panel-Timestamp` 为当前时间的 Unix 时间戳(单位:秒)。'
|
||||
in: header
|
||||
name: 1Panel-Timestamp
|
||||
type: apiKey
|
||||
swagger: "2.0"
|
||||
|
@ -15,8 +15,26 @@ import (
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
// @license.name Apache 2.0
|
||||
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
// @host localhost
|
||||
// @BasePath /api/v1
|
||||
// @schemes http https
|
||||
|
||||
// @securityDefinitions.apikey CustomToken
|
||||
// @description 自定义 Token 格式,格式:md5('1panel' + 1Panel-Token + 1Panel-Timestamp)。
|
||||
// @description ```
|
||||
// @description 示例请求头:
|
||||
// @description curl -X GET "http://localhost:4004/api/v1/resource" \
|
||||
// @description -H "1Panel-Token: <1panel_token>" \
|
||||
// @description -H "1Panel-Timestamp: <current_unix_timestamp>"
|
||||
// @description ```
|
||||
// @description - `1Panel-Token` 为面板 API 接口密钥。
|
||||
// @type apiKey
|
||||
// @in Header
|
||||
// @name 1Panel-Token
|
||||
// @securityDefinitions.apikey Timestamp
|
||||
// @type apiKey
|
||||
// @in header
|
||||
// @name 1Panel-Timestamp
|
||||
// @description - `1Panel-Timestamp` 为当前时间的 Unix 时间戳(单位:秒)。
|
||||
|
||||
//go:generate swag init -o ./docs -g main.go -d ../../backend -g ../cmd/server/main.go
|
||||
func main() {
|
||||
|
@ -58,6 +58,10 @@ export namespace Setting {
|
||||
proxyUser: string;
|
||||
proxyPasswd: string;
|
||||
proxyPasswdKeep: string;
|
||||
|
||||
apiInterfaceStatus: string;
|
||||
apiKey: string;
|
||||
ipWhiteList: string;
|
||||
}
|
||||
export interface SettingUpdate {
|
||||
key: string;
|
||||
@ -186,4 +190,9 @@ export namespace Setting {
|
||||
trial: boolean;
|
||||
status: string;
|
||||
}
|
||||
export interface ApiConfig {
|
||||
apiInterfaceStatus: string;
|
||||
apiKey: string;
|
||||
ipWhiteList: string;
|
||||
}
|
||||
}
|
||||
|
@ -210,3 +210,11 @@ export const loadReleaseNotes = (version: string) => {
|
||||
export const upgrade = (version: string) => {
|
||||
return http.post(`/settings/upgrade`, { version: version });
|
||||
};
|
||||
|
||||
// api config
|
||||
export const generateApiKey = () => {
|
||||
return http.post<string>(`/settings/api/config/generate/key`);
|
||||
};
|
||||
export const updateApiConfig = (param: Setting.ApiConfig) => {
|
||||
return http.post(`/settings/api/config/update`, param);
|
||||
};
|
||||
|
@ -1401,11 +1401,28 @@ const message = {
|
||||
proxyPort: 'Proxy Port',
|
||||
proxyPasswdKeep: 'Remember Password',
|
||||
proxyDocker: 'Docker Proxy',
|
||||
ProxyDockerHelper:
|
||||
proxyDockerHelper:
|
||||
'Synchronize proxy server configuration to Docker, support offline server image pulling and other operations',
|
||||
ConfDockerProxy: 'Configure Docker Proxy',
|
||||
RestartNowHelper: 'Configuring Docker proxy requires restarting the Docker service.',
|
||||
RestartNow: 'Restart immediately',
|
||||
apiInterface: 'API Interface',
|
||||
apiInterfaceClose: 'Once closed, API interfaces cannot be accessed. Do you want to continue?',
|
||||
apiInterfaceHelper: 'Provide panel support for API interface access',
|
||||
apiInterfaceAlert1:
|
||||
'Please do not enable it in production environments as it may increase server security risks',
|
||||
apiInterfaceAlert2:
|
||||
'Please do not use third-party applications to call the panel API to prevent potential security threats.',
|
||||
apiInterfaceAlert3: 'API Interface Document:',
|
||||
apiInterfaceAlert4: 'Usage Document:',
|
||||
apiKey: 'Interface Key',
|
||||
apiKeyHelper: 'Interface key is used for external applications to access API interfaces',
|
||||
ipWhiteList: 'IP Whitelist',
|
||||
ipWhiteListEgs:
|
||||
'When there are multiple IPs, line breaks are required for display, for example: \n172.161.10.111 \n172.161.10.0/24 ',
|
||||
ipWhiteListHelper: 'IPs must be in the IP whitelist list to access the panel API interface',
|
||||
apiKeyReset: 'Interface key reset',
|
||||
apiKeyResetHelper: 'the associated key service will become invalid. Please add a new key to the service',
|
||||
confDockerProxy: 'Configure Docker Proxy',
|
||||
restartNowHelper: 'Configuring Docker proxy requires restarting the Docker service.',
|
||||
restartNow: 'Restart immediately',
|
||||
systemIPWarning: 'The server address is not currently set. Please set it in the control panel first!',
|
||||
systemIPWarning1: 'The current server address is set to {0}, and quick redirection is not possible!',
|
||||
defaultNetwork: 'Network Card',
|
||||
|
@ -1324,6 +1324,20 @@ const message = {
|
||||
proxyPasswdKeep: '記住密碼',
|
||||
proxyDocker: 'Docker 代理',
|
||||
proxyDockerHelper: '將代理伺服器配寘同步至 Docker,支持離線服務器拉取鏡像等操作',
|
||||
apiInterface: 'API 接口',
|
||||
apiInterfaceClose: '關閉後將不能使用 API 接口進行訪問,是否繼續?',
|
||||
apiInterfaceHelper: '提供面板支持 API 接口訪問',
|
||||
apiInterfaceAlert1: '請不要在生產環境開啟,這可能新增服務器安全風險',
|
||||
apiInterfaceAlert2: '請不要使用協力廠商應用調用面板 API,以防止潜在的安全威脅。',
|
||||
apiInterfaceAlert3: 'API 接口檔案:',
|
||||
apiInterfaceAlert4: '使用檔案:',
|
||||
apiKey: '接口密钥',
|
||||
apiKeyHelper: '接口密钥用於外部應用訪問 API 接口',
|
||||
ipWhiteList: 'IP白名單',
|
||||
ipWhiteListEgs: '當存在多個 IP 時,需要換行顯示,例:\n172.16.10.111 \n172.16.10.0/24',
|
||||
ipWhiteListHelper: '必需在 IP 白名單清單中的 IP 才能訪問面板 API 接口',
|
||||
apiKeyReset: '接口密钥重置',
|
||||
apiKeyResetHelper: '重置密钥後,已關聯密钥服務將失效,請重新添加新密鑰至服務。',
|
||||
confDockerProxy: '配寘 Docker 代理',
|
||||
restartNowHelper: '配寘 Docker 代理需要重啓 Docker 服務。',
|
||||
restartNow: '立即重啓',
|
||||
@ -2095,7 +2109,7 @@ const message = {
|
||||
domainHelper: '一行一個網域名稱,支援*和IP位址',
|
||||
pushDir: '推送憑證到本機目錄',
|
||||
dir: '目錄',
|
||||
pushDirHelper: '會在此目錄下產生兩個文件,憑證檔案:fullchain.pem 金鑰檔案:privkey.pem',
|
||||
pushDirHelper: '會在此目錄下產生兩個文件,憑證檔案:fullchain.pem 密钥檔案:privkey.pem',
|
||||
organizationDetail: '機構詳情',
|
||||
fromWebsite: '從網站獲取',
|
||||
dnsMauanlHelper: '手動解析模式需要在建立完之後點選申請按鈕取得 DNS 解析值',
|
||||
|
@ -1326,6 +1326,20 @@ const message = {
|
||||
proxyPasswdKeep: '记住密码',
|
||||
proxyDocker: 'Docker 代理',
|
||||
proxyDockerHelper: '将代理服务器配置同步至 Docker,支持离线服务器拉取镜像等操作',
|
||||
apiInterface: 'API 接口',
|
||||
apiInterfaceClose: '关闭后将不能使用 API 接口进行访问,是否继续?',
|
||||
apiInterfaceHelper: '提供面板支持 API 接口访问',
|
||||
apiInterfaceAlert1: '请不要在生产环境开启,这可能增加服务器安全风险',
|
||||
apiInterfaceAlert2: '请不要使用第三方应用调用面板 API,以防止潜在的安全威胁。',
|
||||
apiInterfaceAlert3: 'API 接口文档:',
|
||||
apiInterfaceAlert4: '使用文档:',
|
||||
apiKey: '接口密钥',
|
||||
apiKeyHelper: '接口密钥用于外部应用访问 API 接口',
|
||||
ipWhiteList: 'IP 白名单',
|
||||
ipWhiteListEgs: '当存在多个 IP 时,需要换行显示,例: \n172.16.10.111 \n172.16.10.0/24',
|
||||
ipWhiteListHelper: '必需在 IP 白名单列表中的 IP 才能访问面板 API 接口',
|
||||
apiKeyReset: '接口密钥重置',
|
||||
apiKeyResetHelper: '重置密钥后,已关联密钥服务将失效,请重新添加新密钥至服务。',
|
||||
confDockerProxy: '配置 Docker 代理',
|
||||
restartNowHelper: '配置 Docker 代理需要重启 Docker 服务。',
|
||||
restartNow: '立即重启',
|
||||
|
@ -96,6 +96,7 @@ html.dark {
|
||||
--panel-terminal-tag-bg-color: var(--panel-main-bg-color-10);
|
||||
--panel-terminal-tag-active-bg-color: var(--panel-main-bg-color-10);
|
||||
--panel-terminal-bg-color: var(--panel-main-bg-color-10);
|
||||
--panel-terminal-tag-active-text-color: var(--panel-color-primary);
|
||||
--panel-logs-bg-color: var(--panel-main-bg-color-9);
|
||||
|
||||
--el-menu-item-bg-color: var(--panel-main-bg-color-10);
|
||||
|
@ -45,6 +45,7 @@ html {
|
||||
--panel-footer-border: #e4e7ed;
|
||||
--panel-terminal-tag-bg-color: #efefef;
|
||||
--panel-terminal-tag-active-bg-color: #575758;
|
||||
--panel-terminal-tag-active-text-color: #ebeef5;
|
||||
--panel-terminal-bg-color: #1e1e1e;
|
||||
--panel-logs-bg-color: #1e1e1e;
|
||||
|
||||
|
@ -160,3 +160,8 @@ defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
:deep(.log-container) {
|
||||
background-color: var(--panel-main-bg-color-10);
|
||||
}
|
||||
</style>
|
||||
|
@ -433,6 +433,7 @@ onMounted(() => {
|
||||
padding: 0;
|
||||
}
|
||||
:deep(.el-tabs__item.is-active) {
|
||||
color: var(--panel-terminal-tag-active-text-color);
|
||||
background-color: var(--panel-terminal-tag-active-bg-color);
|
||||
}
|
||||
}
|
||||
|
@ -228,6 +228,9 @@ const loadDetail = (log: string) => {
|
||||
if (log.indexOf('[MonitorStoreDays]') !== -1) {
|
||||
return log.replace('[MonitorStoreDays]', '[' + i18n.global.t('setting.monitor') + ']');
|
||||
}
|
||||
if (log.indexOf('[ApiInterfaceStatus]') !== -1) {
|
||||
return log.replace('[ApiInterfaceStatus]', '[' + i18n.global.t('setting.apiInterface') + ']');
|
||||
}
|
||||
return log;
|
||||
};
|
||||
|
||||
|
183
frontend/src/views/setting/panel/api-interface/index.vue
Normal file
183
frontend/src/views/setting/panel/api-interface/index.vue
Normal file
@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer
|
||||
v-model="drawerVisible"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
@close="handleClose"
|
||||
size="35%"
|
||||
>
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('setting.apiInterface')" :back="handleClose" />
|
||||
</template>
|
||||
<el-alert class="common-prompt" :closable="false" type="warning">
|
||||
<template #default>
|
||||
<ul>
|
||||
<li>
|
||||
<el-text type="danger">{{ $t('setting.apiInterfaceAlert1') }}</el-text>
|
||||
</li>
|
||||
<li>
|
||||
<el-text type="danger">{{ $t('setting.apiInterfaceAlert2') }}</el-text>
|
||||
</li>
|
||||
<li>
|
||||
{{ $t('setting.apiInterfaceAlert3') }}
|
||||
<el-link :href="apiURL" type="success" target="_blank" class="mb-0.5 ml-0.5">
|
||||
{{ apiURL }}
|
||||
</el-link>
|
||||
</li>
|
||||
<li>
|
||||
{{ $t('setting.apiInterfaceAlert4') }}
|
||||
<el-link :href="panelURL" type="success" target="_blank" class="mb-0.5 ml-0.5">
|
||||
{{ panelURL }}
|
||||
</el-link>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-form
|
||||
:model="form"
|
||||
ref="formRef"
|
||||
@submit.prevent
|
||||
v-loading="loading"
|
||||
label-position="top"
|
||||
:rules="rules"
|
||||
>
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('setting.apiKey')" prop="apiKey">
|
||||
<el-input v-model="form.apiKey" readonly>
|
||||
<template #suffix>
|
||||
<CopyButton type="icon" :content="form.apiKey" class="w-30" />
|
||||
</template>
|
||||
<template #append>
|
||||
<el-button @click="resetApiKey()">
|
||||
{{ $t('commons.button.reset') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('setting.apiKeyHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('setting.ipWhiteList')" prop="ipWhiteList">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:placeholder="$t('setting.ipWhiteListEgs')"
|
||||
:rows="4"
|
||||
v-model="form.ipWhiteList"
|
||||
/>
|
||||
<span class="input-help">{{ $t('setting.ipWhiteListHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button :disabled="loading" type="primary" @click="onBind(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { generateApiKey, updateApiConfig } from '@/api/modules/setting';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { ElMessageBox, FormInstance } from 'element-plus';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
|
||||
const loading = ref();
|
||||
const drawerVisible = ref();
|
||||
const formRef = ref();
|
||||
const apiURL = `${window.location.protocol}//${window.location.hostname}${
|
||||
window.location.port ? `:${window.location.port}` : ''
|
||||
}/1panel/swagger/index.html`;
|
||||
const panelURL = `https://1panel.cn/docs`;
|
||||
|
||||
const form = reactive({
|
||||
apiKey: '',
|
||||
ipWhiteList: '',
|
||||
apiInterfaceStatus: '',
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
ipWhiteList: [Rules.requiredInput],
|
||||
apiKey: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
interface DialogProps {
|
||||
apiInterfaceStatus: string;
|
||||
apiKey: string;
|
||||
ipWhiteList: string;
|
||||
}
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||
form.apiInterfaceStatus = params.apiInterfaceStatus;
|
||||
form.apiKey = params.apiKey;
|
||||
if (params.apiKey == '') {
|
||||
await generateApiKey().then((res) => {
|
||||
form.apiKey = res.data;
|
||||
});
|
||||
}
|
||||
form.ipWhiteList = params.ipWhiteList;
|
||||
drawerVisible.value = true;
|
||||
};
|
||||
|
||||
const resetApiKey = async () => {
|
||||
loading.value = true;
|
||||
ElMessageBox.confirm(i18n.global.t('setting.apiKeyResetHelper'), i18n.global.t('setting.apiKeyReset'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
})
|
||||
.then(async () => {
|
||||
await generateApiKey()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
form.apiKey = res.data;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const onBind = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
let param = {
|
||||
apiKey: form.apiKey,
|
||||
ipWhiteList: form.ipWhiteList,
|
||||
apiInterfaceStatus: form.apiInterfaceStatus,
|
||||
};
|
||||
loading.value = true;
|
||||
await updateApiConfig(param)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
drawerVisible.value = false;
|
||||
emit('search');
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
emit('search');
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
@ -133,6 +133,16 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('setting.apiInterface')" prop="apiInterface">
|
||||
<el-switch
|
||||
@change="onChangeApiInterfaceStatus"
|
||||
v-model="form.apiInterfaceStatus"
|
||||
active-value="enable"
|
||||
inactive-value="disable"
|
||||
/>
|
||||
<span class="input-help">{{ $t('setting.apiInterfaceHelper') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('setting.developerMode')" prop="developerMode">
|
||||
<el-radio-group
|
||||
@change="onSave('DeveloperMode', form.developerMode)"
|
||||
@ -168,6 +178,7 @@
|
||||
<PanelName ref="panelNameRef" @search="search()" />
|
||||
<SystemIP ref="systemIPRef" @search="search()" />
|
||||
<Proxy ref="proxyRef" @search="search()" />
|
||||
<ApiInterface ref="apiInterfaceRef" @search="search()" />
|
||||
<Timeout ref="timeoutRef" @search="search()" />
|
||||
<Network ref="networkRef" @search="search()" />
|
||||
<HideMenu ref="hideMenuRef" @search="search()" />
|
||||
@ -177,7 +188,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { ElForm, ElMessageBox } from 'element-plus';
|
||||
import { getSettingInfo, updateSetting, getSystemAvailable } from '@/api/modules/setting';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
@ -192,6 +203,7 @@ import Proxy from '@/views/setting/panel/proxy/index.vue';
|
||||
import Network from '@/views/setting/panel/default-network/index.vue';
|
||||
import HideMenu from '@/views/setting/panel/hidemenu/index.vue';
|
||||
import ThemeColor from '@/views/setting/panel/theme-color/index.vue';
|
||||
import ApiInterface from '@/views/setting/panel/api-interface/index.vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getXpackSetting, updateXpackSettingByKey } from '@/utils/xpack';
|
||||
import { setPrimaryColor } from '@/utils/theme';
|
||||
@ -241,6 +253,10 @@ const form = reactive({
|
||||
proxyPasswdKeep: '',
|
||||
proxyDocker: '',
|
||||
|
||||
apiInterfaceStatus: 'disable',
|
||||
apiKey: '',
|
||||
ipWhiteList: '',
|
||||
|
||||
proHideMenus: ref(i18n.t('setting.unSetting')),
|
||||
hideMenuList: '',
|
||||
});
|
||||
@ -256,6 +272,7 @@ const timeoutRef = ref();
|
||||
const networkRef = ref();
|
||||
const hideMenuRef = ref();
|
||||
const themeColorRef = ref();
|
||||
const apiInterfaceRef = ref();
|
||||
const unset = ref(i18n.t('setting.unSetting'));
|
||||
|
||||
interface Node {
|
||||
@ -293,6 +310,9 @@ const search = async () => {
|
||||
form.proxyUser = res.data.proxyUser;
|
||||
form.proxyPasswd = res.data.proxyPasswd;
|
||||
form.proxyPasswdKeep = res.data.proxyPasswdKeep;
|
||||
form.apiInterfaceStatus = res.data.apiInterfaceStatus;
|
||||
form.apiKey = res.data.apiKey;
|
||||
form.ipWhiteList = res.data.ipWhiteList;
|
||||
|
||||
const json: Node = JSON.parse(res.data.xpackHideMenu);
|
||||
const checkedTitles = getCheckedTitles(json);
|
||||
@ -361,6 +381,41 @@ const onChangeProxy = () => {
|
||||
proxyDocker: form.proxyDocker,
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeApiInterfaceStatus = () => {
|
||||
if (form.apiInterfaceStatus === 'enable') {
|
||||
apiInterfaceRef.value.acceptParams({
|
||||
apiInterfaceStatus: form.apiInterfaceStatus,
|
||||
apiKey: form.apiKey,
|
||||
ipWhiteList: form.ipWhiteList,
|
||||
});
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm(i18n.t('setting.apiInterfaceClose'), i18n.t('setting.apiInterface'), {
|
||||
confirmButtonText: i18n.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.t('commons.button.cancel'),
|
||||
})
|
||||
.then(async () => {
|
||||
loading.value = true;
|
||||
await updateSetting({ key: 'ApiInterfaceStatus', value: 'disable' })
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
search();
|
||||
MsgSuccess(i18n.t('commons.msg.operationSuccess'));
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
apiInterfaceRef.value.acceptParams({
|
||||
apiInterfaceStatus: 'enable',
|
||||
apiKey: form.apiKey,
|
||||
ipWhiteList: form.ipWhiteList,
|
||||
});
|
||||
return;
|
||||
});
|
||||
};
|
||||
const onChangeNetwork = () => {
|
||||
networkRef.value.acceptParams({ defaultNetwork: form.defaultNetwork });
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user