mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 16:00:51 +00:00
fix: support trusted proxies for API allowlist (#13409)
This commit is contained in:
@@ -816,12 +816,13 @@
|
||||
},
|
||||
"/core/auth/api/update": {
|
||||
"bodyKeys": [
|
||||
"ipWhiteList"
|
||||
"ipWhiteList",
|
||||
"apiTrustedProxies"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList]",
|
||||
"formatEN": "update api config =\u003e IP White List: [ipWhiteList]"
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList], API 可信代理: [apiTrustedProxies]",
|
||||
"formatEN": "update api config =\u003e IP Allowlist: [ipWhiteList], API Trusted Proxies: [apiTrustedProxies]"
|
||||
},
|
||||
"/core/auth/expired/reset": {
|
||||
"bodyKeys": [],
|
||||
@@ -1577,7 +1578,8 @@
|
||||
"/core/enterprise/users/api/update": {
|
||||
"bodyKeys": [
|
||||
"id",
|
||||
"ipWhiteList"
|
||||
"ipWhiteList",
|
||||
"apiTrustedProxies"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"beforeFunctions": [
|
||||
@@ -1590,8 +1592,8 @@
|
||||
"output_value": "name"
|
||||
}
|
||||
],
|
||||
"formatZH": "更新用户 [name] API 接口配置 =\u003e IP 白名单: [ipWhiteList]",
|
||||
"formatEN": "update user [name] api config =\u003e IP White List: [ipWhiteList]"
|
||||
"formatZH": "更新用户 [name] API 接口配置 =\u003e IP 白名单: [ipWhiteList], API 可信代理: [apiTrustedProxies]",
|
||||
"formatEN": "update user [name] api config =\u003e IP Allowlist: [ipWhiteList], API Trusted Proxies: [apiTrustedProxies]"
|
||||
},
|
||||
"/core/enterprise/users/del": {
|
||||
"bodyKeys": [
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
|
||||
appauth "github.com/1Panel-dev/1Panel/core/app/auth"
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
@@ -435,7 +436,7 @@ func (b *BaseApi) GenerateApiKey(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/api/update [post]
|
||||
// @x-panel-log {"bodyKeys":["ipWhiteList"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 API 接口配置 => IP 白名单: [ipWhiteList]","formatEN":"update api config => IP White List: [ipWhiteList]"}
|
||||
// @x-panel-log {"bodyKeys":["ipWhiteList","apiTrustedProxies"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 API 接口配置 => IP 白名单: [ipWhiteList], API 可信代理: [apiTrustedProxies]","formatEN":"update api config => IP Allowlist: [ipWhiteList], API Trusted Proxies: [apiTrustedProxies]"}
|
||||
func (b *BaseApi) UpdateApiConfig(c *gin.Context) {
|
||||
panelToken := c.GetHeader("1Panel-Token")
|
||||
if panelToken != "" {
|
||||
@@ -446,6 +447,12 @@ func (b *BaseApi) UpdateApiConfig(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
trustedProxies, err := appauth.NormalizeAPITrustedProxies(req.ApiTrustedProxies)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
req.ApiTrustedProxies = trustedProxies
|
||||
|
||||
if err := xpack.AuthProvider.UpdateApiConfig(c, req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
|
||||
+123
-1
@@ -6,6 +6,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -24,6 +25,7 @@ type APIAuthConfig struct {
|
||||
ApiInterfaceStatus string
|
||||
ApiKey string
|
||||
IpWhiteList string
|
||||
ApiTrustedProxies string
|
||||
ApiKeyValidityTime int
|
||||
}
|
||||
|
||||
@@ -66,7 +68,7 @@ func APIAuthMiddleware(loadConfig APIAuthConfigLoader, onSuccess APIAuthSuccessH
|
||||
helper.BadAuth(c, "ErrApiConfigKeyInvalid", nil)
|
||||
return
|
||||
}
|
||||
if !isIPInWhiteList(c.ClientIP(), config.IpWhiteList) {
|
||||
if !isIPInWhiteList(GetAPIClientIP(c, config.ApiTrustedProxies), config.IpWhiteList) {
|
||||
helper.BadAuth(c, "ErrApiConfigIPInvalid", nil)
|
||||
return
|
||||
}
|
||||
@@ -92,6 +94,9 @@ func LoadAPIAuthConfig(_ *gin.Context) (APIAuthConfig, error) {
|
||||
if config.IpWhiteList, err = settingRepo.GetValueByKey("IpWhiteList"); err != nil {
|
||||
return config, err
|
||||
}
|
||||
if config.ApiTrustedProxies, err = settingRepo.GetValueByKey("ApiTrustedProxies"); err != nil {
|
||||
return config, err
|
||||
}
|
||||
apiValidity, err := settingRepo.GetValueByKey("ApiKeyValidityTime")
|
||||
if err != nil {
|
||||
return config, err
|
||||
@@ -102,6 +107,123 @@ func LoadAPIAuthConfig(_ *gin.Context) (APIAuthConfig, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func GetAPIClientIP(c *gin.Context, trustedProxies string) string {
|
||||
remoteAddr := common.GetRealClientIP(c)
|
||||
remoteIP := net.ParseIP(remoteAddr)
|
||||
if remoteIP == nil {
|
||||
return remoteAddr
|
||||
}
|
||||
|
||||
proxies, err := parseAPITrustedProxies(trustedProxies)
|
||||
if err != nil {
|
||||
if global.LOG != nil {
|
||||
global.LOG.Errorf("Failed to parse API trusted proxies: %v", err)
|
||||
}
|
||||
return remoteAddr
|
||||
}
|
||||
if !isIPInNetworks(remoteIP, proxies) {
|
||||
return remoteAddr
|
||||
}
|
||||
|
||||
forwardedFor := strings.Join(c.Request.Header.Values("X-Forwarded-For"), ",")
|
||||
if strings.TrimSpace(forwardedFor) != "" {
|
||||
clientIP, ok := clientIPFromForwardedFor(forwardedFor, proxies)
|
||||
if !ok {
|
||||
return remoteAddr
|
||||
}
|
||||
return clientIP
|
||||
}
|
||||
|
||||
realIPValue := strings.Join(c.Request.Header.Values("X-Real-IP"), ",")
|
||||
realIP := net.ParseIP(strings.TrimSpace(realIPValue))
|
||||
if realIP == nil {
|
||||
return remoteAddr
|
||||
}
|
||||
return realIP.String()
|
||||
}
|
||||
|
||||
func NormalizeAPITrustedProxies(value string) (string, error) {
|
||||
lines := strings.Split(value, "\n")
|
||||
normalized := make([]string, 0, len(lines))
|
||||
for _, line := range lines {
|
||||
item := strings.TrimSpace(line)
|
||||
if item == "" {
|
||||
continue
|
||||
}
|
||||
if ip := net.ParseIP(item); ip != nil {
|
||||
normalized = append(normalized, ip.String())
|
||||
continue
|
||||
}
|
||||
_, ipNet, err := net.ParseCIDR(item)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid API trusted proxy entry %q: %w", item, err)
|
||||
}
|
||||
ones, _ := ipNet.Mask.Size()
|
||||
if ones == 0 {
|
||||
return "", fmt.Errorf("invalid API trusted proxy entry %q: unrestricted CIDR is not allowed", item)
|
||||
}
|
||||
normalized = append(normalized, ipNet.String())
|
||||
}
|
||||
return strings.Join(normalized, "\n"), nil
|
||||
}
|
||||
|
||||
func parseAPITrustedProxies(value string) ([]*net.IPNet, error) {
|
||||
normalized, err := NormalizeAPITrustedProxies(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if normalized == "" {
|
||||
return []*net.IPNet{}, nil
|
||||
}
|
||||
|
||||
lines := strings.Split(normalized, "\n")
|
||||
proxies := make([]*net.IPNet, 0, len(lines))
|
||||
for _, item := range lines {
|
||||
if ip := net.ParseIP(item); ip != nil {
|
||||
bits := 128
|
||||
if ip.To4() != nil {
|
||||
bits = 32
|
||||
ip = ip.To4()
|
||||
}
|
||||
proxies = append(proxies, &net.IPNet{IP: ip, Mask: net.CIDRMask(bits, bits)})
|
||||
continue
|
||||
}
|
||||
_, ipNet, err := net.ParseCIDR(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proxies = append(proxies, ipNet)
|
||||
}
|
||||
return proxies, nil
|
||||
}
|
||||
|
||||
func clientIPFromForwardedFor(value string, trustedProxies []*net.IPNet) (string, bool) {
|
||||
items := strings.Split(value, ",")
|
||||
ips := make([]net.IP, len(items))
|
||||
for i, item := range items {
|
||||
ip := net.ParseIP(strings.TrimSpace(item))
|
||||
if ip == nil {
|
||||
return "", false
|
||||
}
|
||||
ips[i] = ip
|
||||
}
|
||||
for i := len(ips) - 1; i >= 0; i-- {
|
||||
if i == 0 || !isIPInNetworks(ips[i], trustedProxies) {
|
||||
return ips[i].String(), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func isIPInNetworks(ip net.IP, networks []*net.IPNet) bool {
|
||||
for _, network := range networks {
|
||||
if network.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValid1PanelTimestamp(panelTimestamp string, apiKeyValidityTime int) bool {
|
||||
apiTime := apiKeyValidityTime
|
||||
if apiTime < 0 {
|
||||
|
||||
@@ -311,6 +311,10 @@ func GenerateApiKey() (string, error) {
|
||||
}
|
||||
func UpdateApiConfig(req dto.ApiInterfaceConfig) error {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
trustedProxies, err := NormalizeAPITrustedProxies(req.ApiTrustedProxies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("ApiInterfaceStatus", req.ApiInterfaceStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -320,6 +324,9 @@ func UpdateApiConfig(req dto.ApiInterfaceConfig) error {
|
||||
if err := settingRepo.UpdateOrCreate("IpWhiteList", req.IpWhiteList); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("ApiTrustedProxies", trustedProxies); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("ApiKeyValidityTime", strconv.Itoa(req.ApiKeyValidityTime)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ type ApiInterfaceConfig struct {
|
||||
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
IpWhiteList string `json:"ipWhiteList"`
|
||||
ApiTrustedProxies string `json:"apiTrustedProxies"`
|
||||
ApiKeyValidityTime int `json:"apiKeyValidityTime"`
|
||||
}
|
||||
|
||||
@@ -71,6 +72,7 @@ type CurrentUserInfo struct {
|
||||
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
IpWhiteList string `json:"ipWhiteList"`
|
||||
ApiTrustedProxies string `json:"apiTrustedProxies"`
|
||||
ApiKeyValidityTime int `json:"apiKeyValidityTime"`
|
||||
|
||||
Role string `json:"role"`
|
||||
|
||||
@@ -2120,6 +2120,158 @@ const docTemplate = `{
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/plugins/install": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.AgentPluginMarketInstallReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"summary": "Install an OpenClaw marketplace plugin",
|
||||
"tags": [
|
||||
"AI"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/plugins/list": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.AgentPluginsReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.AgentPluginItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"summary": "List OpenClaw plugins",
|
||||
"tags": [
|
||||
"AI"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/plugins/operate": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.AgentPluginOperateReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"summary": "Operate an OpenClaw plugin",
|
||||
"tags": [
|
||||
"AI"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/plugins/search": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.AgentPluginSearchReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.AgentPluginSearchItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"summary": "Search OpenClaw plugins",
|
||||
"tags": [
|
||||
"AI"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/remark": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
@@ -9257,10 +9409,11 @@ const docTemplate = `{
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"ipWhiteList"
|
||||
"ipWhiteList",
|
||||
"apiTrustedProxies"
|
||||
],
|
||||
"formatEN": "update api config =\u003e IP White List: [ipWhiteList]",
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList]",
|
||||
"formatEN": "update api config =\u003e IP Allowlist: [ipWhiteList], API Trusted Proxies: [apiTrustedProxies]",
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList], API 可信代理: [apiTrustedProxies]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@@ -30404,6 +30557,143 @@ const docTemplate = `{
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginItem": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"origin": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginMarketInstallReq": {
|
||||
"properties": {
|
||||
"agentId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"package": {
|
||||
"maxLength": 200,
|
||||
"type": "string"
|
||||
},
|
||||
"taskID": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"maxLength": 100,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agentId",
|
||||
"package",
|
||||
"taskID",
|
||||
"version"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginOperateReq": {
|
||||
"properties": {
|
||||
"agentId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"operate": {
|
||||
"enum": [
|
||||
"enable",
|
||||
"disable",
|
||||
"update",
|
||||
"uninstall"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"pluginId": {
|
||||
"maxLength": 200,
|
||||
"type": "string"
|
||||
},
|
||||
"taskID": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agentId",
|
||||
"operate",
|
||||
"pluginId",
|
||||
"taskID"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginSearchItem": {
|
||||
"properties": {
|
||||
"categories": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"downloads": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"official": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"pluginId": {
|
||||
"type": "string"
|
||||
},
|
||||
"score": {
|
||||
"type": "number"
|
||||
},
|
||||
"verificationTier": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginSearchReq": {
|
||||
"properties": {
|
||||
"agentId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"keyword": {
|
||||
"maxLength": 100,
|
||||
"type": "string"
|
||||
},
|
||||
"limit": {
|
||||
"maximum": 100,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agentId",
|
||||
"keyword"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginStatus": {
|
||||
"properties": {
|
||||
"currentVersion": {
|
||||
@@ -30473,6 +30763,17 @@ const docTemplate = `{
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginsReq": {
|
||||
"properties": {
|
||||
"agentId": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agentId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentQQBotBot": {
|
||||
"properties": {
|
||||
"accountId": {
|
||||
@@ -31518,6 +31819,9 @@ const docTemplate = `{
|
||||
"apiKeyValidityTime": {
|
||||
"type": "integer"
|
||||
},
|
||||
"apiTrustedProxies": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipWhiteList": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -33560,6 +33864,15 @@ const docTemplate = `{
|
||||
"apiKeyValidityTime": {
|
||||
"type": "integer"
|
||||
},
|
||||
"apiTrustedProxies": {
|
||||
"type": "string"
|
||||
},
|
||||
"authSource": {
|
||||
"type": "string"
|
||||
},
|
||||
"authSourceStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"complexitySetting": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -2116,6 +2116,158 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/plugins/install": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.AgentPluginMarketInstallReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"summary": "Install an OpenClaw marketplace plugin",
|
||||
"tags": [
|
||||
"AI"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/plugins/list": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.AgentPluginsReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.AgentPluginItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"summary": "List OpenClaw plugins",
|
||||
"tags": [
|
||||
"AI"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/plugins/operate": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.AgentPluginOperateReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"summary": "Operate an OpenClaw plugin",
|
||||
"tags": [
|
||||
"AI"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/plugins/search": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.AgentPluginSearchReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.AgentPluginSearchItem"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
},
|
||||
{
|
||||
"Timestamp": []
|
||||
}
|
||||
],
|
||||
"summary": "Search OpenClaw plugins",
|
||||
"tags": [
|
||||
"AI"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/ai/agents/remark": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
@@ -9253,10 +9405,11 @@
|
||||
"x-panel-log": {
|
||||
"BeforeFunctions": [],
|
||||
"bodyKeys": [
|
||||
"ipWhiteList"
|
||||
"ipWhiteList",
|
||||
"apiTrustedProxies"
|
||||
],
|
||||
"formatEN": "update api config =\u003e IP White List: [ipWhiteList]",
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList]",
|
||||
"formatEN": "update api config =\u003e IP Allowlist: [ipWhiteList], API Trusted Proxies: [apiTrustedProxies]",
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList], API 可信代理: [apiTrustedProxies]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
@@ -30400,6 +30553,143 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginItem": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"origin": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginMarketInstallReq": {
|
||||
"properties": {
|
||||
"agentId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"package": {
|
||||
"maxLength": 200,
|
||||
"type": "string"
|
||||
},
|
||||
"taskID": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"maxLength": 100,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agentId",
|
||||
"package",
|
||||
"taskID",
|
||||
"version"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginOperateReq": {
|
||||
"properties": {
|
||||
"agentId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"operate": {
|
||||
"enum": [
|
||||
"enable",
|
||||
"disable",
|
||||
"update",
|
||||
"uninstall"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"pluginId": {
|
||||
"maxLength": 200,
|
||||
"type": "string"
|
||||
},
|
||||
"taskID": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agentId",
|
||||
"operate",
|
||||
"pluginId",
|
||||
"taskID"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginSearchItem": {
|
||||
"properties": {
|
||||
"categories": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"downloads": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"official": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"pluginId": {
|
||||
"type": "string"
|
||||
},
|
||||
"score": {
|
||||
"type": "number"
|
||||
},
|
||||
"verificationTier": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginSearchReq": {
|
||||
"properties": {
|
||||
"agentId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"keyword": {
|
||||
"maxLength": 100,
|
||||
"type": "string"
|
||||
},
|
||||
"limit": {
|
||||
"maximum": 100,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agentId",
|
||||
"keyword"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginStatus": {
|
||||
"properties": {
|
||||
"currentVersion": {
|
||||
@@ -30469,6 +30759,17 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentPluginsReq": {
|
||||
"properties": {
|
||||
"agentId": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"agentId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"dto.AgentQQBotBot": {
|
||||
"properties": {
|
||||
"accountId": {
|
||||
@@ -31514,6 +31815,9 @@
|
||||
"apiKeyValidityTime": {
|
||||
"type": "integer"
|
||||
},
|
||||
"apiTrustedProxies": {
|
||||
"type": "string"
|
||||
},
|
||||
"ipWhiteList": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -33556,6 +33860,15 @@
|
||||
"apiKeyValidityTime": {
|
||||
"type": "integer"
|
||||
},
|
||||
"apiTrustedProxies": {
|
||||
"type": "string"
|
||||
},
|
||||
"authSource": {
|
||||
"type": "string"
|
||||
},
|
||||
"authSourceStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"complexitySetting": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -816,12 +816,13 @@
|
||||
},
|
||||
"/core/auth/api/update": {
|
||||
"bodyKeys": [
|
||||
"ipWhiteList"
|
||||
"ipWhiteList",
|
||||
"apiTrustedProxies"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList]",
|
||||
"formatEN": "update api config =\u003e IP White List: [ipWhiteList]"
|
||||
"formatZH": "更新 API 接口配置 =\u003e IP 白名单: [ipWhiteList], API 可信代理: [apiTrustedProxies]",
|
||||
"formatEN": "update api config =\u003e IP Allowlist: [ipWhiteList], API Trusted Proxies: [apiTrustedProxies]"
|
||||
},
|
||||
"/core/auth/expired/reset": {
|
||||
"bodyKeys": [],
|
||||
@@ -1577,7 +1578,8 @@
|
||||
"/core/enterprise/users/api/update": {
|
||||
"bodyKeys": [
|
||||
"id",
|
||||
"ipWhiteList"
|
||||
"ipWhiteList",
|
||||
"apiTrustedProxies"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"beforeFunctions": [
|
||||
@@ -1590,8 +1592,8 @@
|
||||
"output_value": "name"
|
||||
}
|
||||
],
|
||||
"formatZH": "更新用户 [name] API 接口配置 =\u003e IP 白名单: [ipWhiteList]",
|
||||
"formatEN": "update user [name] api config =\u003e IP White List: [ipWhiteList]"
|
||||
"formatZH": "更新用户 [name] API 接口配置 =\u003e IP 白名单: [ipWhiteList], API 可信代理: [apiTrustedProxies]",
|
||||
"formatEN": "update user [name] api config =\u003e IP Allowlist: [ipWhiteList], API Trusted Proxies: [apiTrustedProxies]"
|
||||
},
|
||||
"/core/enterprise/users/del": {
|
||||
"bodyKeys": [
|
||||
|
||||
@@ -51,6 +51,7 @@ func Init() {
|
||||
migrations.AddLoginLogUser,
|
||||
migrations.AddAlertAuditUser,
|
||||
migrations.AddMenuAccordionSetting,
|
||||
migrations.AddAPITrustedProxiesSetting,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -183,6 +183,9 @@ var InitSetting = &gormigrate.Migration{
|
||||
if err := tx.Create(&model.Setting{Key: "IpWhiteList", Value: ""}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "ApiTrustedProxies", Value: ""}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "ApiKeyValidityTime", Value: "120"}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1282,3 +1285,17 @@ var AddMenuAccordionSetting = &gormigrate.Migration{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddAPITrustedProxiesSetting = &gormigrate.Migration{
|
||||
ID: "20260729-add-api-trusted-proxies-setting",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
var setting model.Setting
|
||||
if err := tx.Where("key = ?", "ApiTrustedProxies").First(&setting).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return tx.Create(&model.Setting{Key: "ApiTrustedProxies", Value: ""}).Error
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ export namespace Login {
|
||||
apiInterfaceStatus: string;
|
||||
apiKey: string;
|
||||
ipWhiteList: string;
|
||||
apiTrustedProxies: string;
|
||||
apiKeyValidityTime: number;
|
||||
}
|
||||
export interface AuthInfoUpdate {
|
||||
@@ -86,6 +87,7 @@ export namespace Login {
|
||||
apiInterfaceStatus: string;
|
||||
apiKey: string;
|
||||
ipWhiteList: string;
|
||||
apiTrustedProxies: string;
|
||||
apiKeyValidityTime: number;
|
||||
}
|
||||
export interface PasswordUpdate {
|
||||
|
||||
@@ -2758,6 +2758,9 @@ const message = {
|
||||
ipWhiteList: 'IP allowlist',
|
||||
ipWhiteListEgs: 'One per line. For example,\n172.161.10.111\n172.161.10.0/24',
|
||||
ipWhiteListHelper: 'IPs within the allowlist can access the API,0.0.0.0/0 (all IPv4), ::/0 (all IPv6)',
|
||||
apiTrustedProxies: 'API trusted proxies',
|
||||
apiTrustedProxiesHelper:
|
||||
'When using a reverse proxy, enter the IP address or network of the proxy server to correctly obtain the client IP. Otherwise, leave this blank. 0.0.0.0/0 and ::/0 are not supported.',
|
||||
apiKeyValidityTime: 'Validity period of interface key',
|
||||
apiKeyValidityTimeEgs: 'Validity period of interface key (in minutes)',
|
||||
apiKeyValidityTimeHelper:
|
||||
|
||||
@@ -2807,6 +2807,9 @@ const message = {
|
||||
ipWhiteListEgs: 'Una por línea. Por ejemplo,\n172.161.10.111\n172.161.10.0/24',
|
||||
ipWhiteListHelper:
|
||||
'Las IP en la lista blanca pueden acceder a la API, 0.0.0.0/0 (todas IPv4), ::/0 (todas IPv6)',
|
||||
apiTrustedProxies: 'Proxies de confianza de la API',
|
||||
apiTrustedProxiesHelper:
|
||||
'Al usar un proxy inverso, introduce la IP o la red del servidor proxy para obtener correctamente la IP del cliente. Si no lo usas, deja este campo vacío. No se admiten 0.0.0.0/0 ni ::/0.',
|
||||
apiKeyValidityTime: 'Periodo de validez de la clave de interfaz',
|
||||
apiKeyValidityTimeEgs: 'Periodo de validez de la clave de interfaz (en minutos)',
|
||||
apiKeyValidityTimeHelper:
|
||||
|
||||
@@ -2733,6 +2733,9 @@ const message = {
|
||||
ipWhiteListEgs: 'هر خط یک IP. مثلاً،\n172.161.10.111\n172.161.10.0/24',
|
||||
ipWhiteListHelper:
|
||||
'IPهای موجود در لیست سفید میتوانند به API دسترسی داشته باشند، 0.0.0.0/0 (همه IPv4)، ::/0 (همه IPv6)',
|
||||
apiTrustedProxies: 'پروکسیهای قابل اعتماد API',
|
||||
apiTrustedProxiesHelper:
|
||||
'هنگام استفاده از پروکسی معکوس، IP یا شبکه سرور پروکسی را وارد کنید تا IP کلاینت بهدرستی دریافت شود. در صورت عدم استفاده، این بخش را خالی بگذارید. 0.0.0.0/0 و ::/0 پشتیبانی نمیشوند.',
|
||||
apiKeyValidityTime: 'مدت اعتبار کلید رابط',
|
||||
apiKeyValidityTimeEgs: 'مدت اعتبار کلید رابط (به دقیقه)',
|
||||
apiKeyValidityTimeHelper:
|
||||
|
||||
@@ -2755,6 +2755,9 @@ const message = {
|
||||
ipWhiteList: 'IP AllowList',
|
||||
ipWhiteListEgs: '1行に1つ。たとえば、 n172.161.10.111 n172.161.10.0/24',
|
||||
ipWhiteListHelper: 'AllowList内のIPSはAPIにアクセスできます、0.0.0.0/0(すべての IPv4)、::/0(すべての IPv6)',
|
||||
apiTrustedProxies: 'API信頼済みプロキシ',
|
||||
apiTrustedProxiesHelper:
|
||||
'リバースプロキシを使用する場合は、クライアントIPを正しく取得するために、プロキシサーバーのIPまたはネットワークを入力してください。使用しない場合は空欄にしてください。0.0.0.0/0と::/0は指定できません。',
|
||||
apiKeyReset: 'インターフェイスキーリセット',
|
||||
apiKeyResetHelper: '関連するキーサービスは無効になります。サービスに新しいキーを追加してください',
|
||||
confDockerProxy: 'Dockerプロキシを構成します',
|
||||
|
||||
@@ -2706,6 +2706,9 @@ const message = {
|
||||
ipWhiteList: 'IP 허용 목록',
|
||||
ipWhiteListEgs: '한 줄에 하나씩 입력하십시오. 예:\n172.161.10.111\n172.161.10.0/24',
|
||||
ipWhiteListHelper: '허용 목록에 있는 IP만 API 에 접근할 수 있습니다. 0.0.0.0/0(모든 IPv4), ::/0(모든 IPv6)',
|
||||
apiTrustedProxies: 'API 신뢰 프록시',
|
||||
apiTrustedProxiesHelper:
|
||||
'리버스 프록시를 사용하는 경우 클라이언트 IP를 올바르게 가져오려면 프록시 서버의 IP 또는 네트워크를 입력하세요. 사용하지 않는 경우 비워 두세요. 0.0.0.0/0 및 ::/0은 지원되지 않습니다.',
|
||||
apiKeyValidityTime: '인터페이스 키 유효 기간',
|
||||
apiKeyValidityTimeEgs: '인터페이스 키 유효 기간 (분 단위)',
|
||||
apiKeyValidityTimeHelper:
|
||||
|
||||
@@ -2696,6 +2696,9 @@ const message = {
|
||||
ipWhiteList: 'IP allowlist',
|
||||
ipWhiteListEgs: 'ໜຶ່ງລາຍການຕໍ່ແຖວ. ຕົວຢ່າງ:\n172.161.10.111\n172.161.10.0/24',
|
||||
ipWhiteListHelper: 'IP ໃນ allowlist ສາມາດເຂົ້າເຖິງ API ໄດ້, 0.0.0.0/0 (ທຸກ IPv4), ::/0 (ທຸກ IPv6)',
|
||||
apiTrustedProxies: 'ພຣັອກຊີ API ທີ່ເຊື່ອຖື',
|
||||
apiTrustedProxiesHelper:
|
||||
'ເມື່ອໃຊ້ reverse proxy, ໃຫ້ໃສ່ IP ຫຼືເຄືອຂ່າຍຂອງ proxy server ເພື່ອໃຫ້ໄດ້ client IP ຢ່າງຖືກຕ້ອງ. ຖ້າບໍ່ໃຊ້ໃຫ້ປ່ອຍຫວ່າງ. ບໍ່ຮອງຮັບ 0.0.0.0/0 ແລະ ::/0.',
|
||||
apiKeyValidityTime: 'ໄລຍະເວລາທີ່ API key ໃຊ້ງານໄດ້',
|
||||
apiKeyValidityTimeEgs: 'ໄລຍະເວລາທີ່ API key ໃຊ້ງານໄດ້ (ນາທີ)',
|
||||
apiKeyValidityTimeHelper:
|
||||
|
||||
@@ -2803,6 +2803,9 @@ const message = {
|
||||
ipWhiteList: 'Senarai putih IP',
|
||||
ipWhiteListEgs: 'Satu per baris. Contoh,\n172.161.10.111\n172.161.10.0/24',
|
||||
ipWhiteListHelper: 'IP dalam senarai putih boleh mengakses API, 0.0.0.0/0 (semua IPv4), ::/0 (semua IPv6)',
|
||||
apiTrustedProxies: 'Proksi dipercayai API',
|
||||
apiTrustedProxiesHelper:
|
||||
'Apabila menggunakan proksi songsang, masukkan IP atau rangkaian pelayan proksi untuk mendapatkan IP klien dengan betul. Jika tidak digunakan, biarkan kosong. 0.0.0.0/0 dan ::/0 tidak disokong.',
|
||||
apiKeyValidityTime: 'Tempoh sah kunci antara muka',
|
||||
apiKeyValidityTimeEgs: 'Tempoh sah kunci antara muka (dalam minit)',
|
||||
apiKeyValidityTimeHelper:
|
||||
|
||||
@@ -2919,6 +2919,9 @@ const message = {
|
||||
ipWhiteListEgs: 'Um por linha. Exemplo: \n172.161.10.111\n172.161.10.0/24',
|
||||
ipWhiteListHelper:
|
||||
'IPs na lista de permitidos podem acessar a API, 0.0.0.0/0 (todos os IPv4), ::/0 (todos os IPv6)',
|
||||
apiTrustedProxies: 'Proxies confiáveis da API',
|
||||
apiTrustedProxiesHelper:
|
||||
'Ao usar um proxy reverso, insira o IP ou a rede do servidor proxy para obter corretamente o IP do cliente. Caso contrário, deixe este campo em branco. 0.0.0.0/0 e ::/0 não são suportados.',
|
||||
apiKeyReset: 'Redefinir chave da interface',
|
||||
apiKeyResetHelper:
|
||||
'O serviço associado à chave se tornará inválido. Por favor, adicione uma nova chave ao serviço',
|
||||
|
||||
@@ -2778,6 +2778,9 @@ const message = {
|
||||
ipWhiteListEgs: 'По одному в строке. Например,\n172.161.10.111\n172.161.10.0/24',
|
||||
ipWhiteListHelper:
|
||||
'IP-адреса из белого списка могут получить доступ к API, 0.0.0.0/0 (все IPv4), ::/0 (все IPv6)',
|
||||
apiTrustedProxies: 'Доверенные прокси API',
|
||||
apiTrustedProxiesHelper:
|
||||
'При использовании обратного прокси укажите IP-адрес или сеть прокси-сервера, чтобы корректно определить IP клиента. Если прокси не используется, оставьте поле пустым. 0.0.0.0/0 и ::/0 не поддерживаются.',
|
||||
apiKeyValidityTime: 'Срок действия ключа интерфейса',
|
||||
apiKeyValidityTimeEgs: 'Срок действия ключа интерфейса (в единицах)',
|
||||
apiKeyValidityTimeHelper:
|
||||
|
||||
@@ -2786,6 +2786,9 @@ const message = {
|
||||
ipWhiteList: 'IP izin listesi',
|
||||
ipWhiteListEgs: 'Her satıra bir tane. Örneğin,\n172.161.10.111\n172.161.10.0/24',
|
||||
ipWhiteListHelper: 'İzin listesindeki IP’ler API’ye erişebilir, 0.0.0.0/0 (tüm IPv4), ::/0 (tüm IPv6)',
|
||||
apiTrustedProxies: 'API güvenilir proxyleri',
|
||||
apiTrustedProxiesHelper:
|
||||
'Ters proxy kullanırken istemci IP adresini doğru şekilde almak için proxy sunucusunun IP adresini veya ağını girin. Kullanmıyorsanız boş bırakın. 0.0.0.0/0 ve ::/0 desteklenmez.',
|
||||
apiKeyValidityTime: 'Arayüz anahtarının geçerlilik süresi',
|
||||
apiKeyValidityTimeEgs: 'Arayüz anahtarının geçerlilik süresi (dakika cinsinden)',
|
||||
apiKeyValidityTimeHelper:
|
||||
|
||||
@@ -2587,6 +2587,9 @@ const message = {
|
||||
ipWhiteListEgs: '當存在多個 IP 時,需要換行顯示,例:\n172.16.10.111 \n172.16.10.0/24',
|
||||
ipWhiteListHelper:
|
||||
'必需在 IP 白名單清單中的 IP 才能存取面板 API 介面,0.0.0.0/0(所有 IPv4),::/0(所有 IPv6)',
|
||||
apiTrustedProxies: 'API 可信代理',
|
||||
apiTrustedProxiesHelper:
|
||||
'使用反向代理時,請填寫代理伺服器的 IP 或網段,以便正確取得用戶端 IP;不使用時請留空。不支援 0.0.0.0/0 和 ::/0。',
|
||||
apiKeyValidityTime: '介面金鑰有效期',
|
||||
apiKeyValidityTimeEgs: '介面金鑰有效期(組織分)',
|
||||
apiKeyValidityTimeHelper: '介面時間戳記到請求時的目前時間戳之間有效(組織分),設定為0時,不做時間戳記校驗',
|
||||
|
||||
@@ -2582,6 +2582,9 @@ const message = {
|
||||
ipWhiteListEgs: '当存在多个 IP 时,需要换行显示,例: \n172.16.10.111 \n172.16.10.0/24',
|
||||
ipWhiteListHelper:
|
||||
'必需在 IP 白名单列表中的 IP 才能访问面板 API 接口,0.0.0.0/0(所有 IPv4),::/0(所有 IPv6)',
|
||||
apiTrustedProxies: 'API 可信代理',
|
||||
apiTrustedProxiesHelper:
|
||||
'使用反向代理时,请填写代理服务器的 IP 或网段,以便正确获取客户端 IP;不使用时请留空。不支持 0.0.0.0/0 和 ::/0。',
|
||||
apiKeyValidityTime: '接口密钥有效期',
|
||||
apiKeyValidityTimeEgs: '接口密钥有效期(单位分)',
|
||||
apiKeyValidityTimeHelper: '接口时间戳到请求时的当前时间戳之间有效(单位分),设置为 0 时,不做时间戳校验',
|
||||
|
||||
@@ -356,6 +356,15 @@
|
||||
/>
|
||||
<span class="input-help">{{ $t('setting.ipWhiteListHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('setting.apiTrustedProxies')" prop="apiTrustedProxies">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:placeholder="$t('setting.allowIPEgs')"
|
||||
:rows="3"
|
||||
v-model="form.apiTrustedProxies"
|
||||
/>
|
||||
<span class="input-help">{{ $t('setting.apiTrustedProxiesHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('setting.apiKeyValidityTime')" prop="apiKeyValidityTime">
|
||||
<el-input :placeholder="$t('setting.apiKeyValidityTimeEgs')" v-model.number="form.apiKeyValidityTime">
|
||||
<template #append>{{ $t('commons.units.minute') }}</template>
|
||||
@@ -446,6 +455,7 @@ const form = reactive({
|
||||
apiInterfaceStatus: 'Disable',
|
||||
apiKey: '',
|
||||
ipWhiteList: '',
|
||||
apiTrustedProxies: '',
|
||||
apiKeyValidityTime: 120,
|
||||
});
|
||||
const mfaForm = reactive({
|
||||
@@ -512,6 +522,7 @@ const mfaRules = reactive({
|
||||
});
|
||||
const apiRules = reactive({
|
||||
ipWhiteList: [Rules.requiredInput, { validator: checkIPs, trigger: 'blur' }],
|
||||
apiTrustedProxies: [{ validator: checkIPs, trigger: 'blur' }],
|
||||
apiKey: [Rules.requiredInput],
|
||||
apiKeyValidityTime: [Rules.requiredInput, Rules.integerNumberWith0],
|
||||
});
|
||||
@@ -523,7 +534,7 @@ const getUserFormFields = () => {
|
||||
}
|
||||
return fields;
|
||||
};
|
||||
const apiFormFields = ['apiKey', 'ipWhiteList', 'apiKeyValidityTime'];
|
||||
const apiFormFields = ['apiKey', 'ipWhiteList', 'apiTrustedProxies', 'apiKeyValidityTime'];
|
||||
|
||||
const openDrawer = async () => {
|
||||
if (!props.currentUser) {
|
||||
@@ -538,6 +549,7 @@ const syncApiConfig = (currentUser: Login.AuthInfo) => {
|
||||
form.apiInterfaceStatus = currentUser.apiInterfaceStatus || 'Disable';
|
||||
form.apiKey = currentUser.apiKey;
|
||||
form.ipWhiteList = currentUser.ipWhiteList;
|
||||
form.apiTrustedProxies = currentUser.apiTrustedProxies || '';
|
||||
form.apiKeyValidityTime = currentUser.apiKeyValidityTime;
|
||||
savedApiStatus.value = form.apiInterfaceStatus;
|
||||
};
|
||||
@@ -586,9 +598,10 @@ const resetApiKey = async () => {
|
||||
};
|
||||
|
||||
function checkIPs(rule: any, value: any, callback: any) {
|
||||
if (form.ipWhiteList !== '') {
|
||||
let addr = form.ipWhiteList.split('\n');
|
||||
for (const item of addr) {
|
||||
if (value !== '') {
|
||||
let addr = value.split('\n');
|
||||
for (const rawItem of addr) {
|
||||
const item = rawItem.trim();
|
||||
if (item === '') {
|
||||
continue;
|
||||
}
|
||||
@@ -898,6 +911,7 @@ const onSaveApi = async (formEl: FormInstance | undefined) => {
|
||||
const param = {
|
||||
apiKey: form.apiKey,
|
||||
ipWhiteList: form.ipWhiteList,
|
||||
apiTrustedProxies: form.apiTrustedProxies,
|
||||
apiInterfaceStatus: form.apiInterfaceStatus,
|
||||
apiKeyValidityTime: form.apiKeyValidityTime,
|
||||
};
|
||||
@@ -972,6 +986,7 @@ const handleApi = async () => {
|
||||
let param = {
|
||||
apiKey: form.apiKey,
|
||||
ipWhiteList: form.ipWhiteList,
|
||||
apiTrustedProxies: form.apiTrustedProxies,
|
||||
apiInterfaceStatus: form.apiInterfaceStatus,
|
||||
apiKeyValidityTime: form.apiKeyValidityTime,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user