mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
feat: refactor auth and xpack integration
This commit is contained in:
committed by
zhengkunwang223
parent
69906046e8
commit
a917ca00a1
10
.gitignore
vendored
10
.gitignore
vendored
@@ -42,9 +42,17 @@ frontend/components.d.ts
|
||||
frontend/src/xpack
|
||||
frontend/src/xpack-ee
|
||||
agent/xpack
|
||||
agent/xpack-ee
|
||||
agent/router/entry_xpack.go
|
||||
agent/server/init_xpack.go
|
||||
agent/utils/xpack/xpack.go
|
||||
core/xpack
|
||||
core/xpack-ee
|
||||
core/router/entry_xpack.go
|
||||
core/router/entry_xpackee.go
|
||||
core/server/init_xpack.go
|
||||
core/server/init_xpackee.go
|
||||
core/utils/xpack/xpack.go
|
||||
core/utils/xpack/xpackee.go
|
||||
|
||||
.history/
|
||||
dist/
|
||||
|
||||
@@ -3,9 +3,6 @@ package v2
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -163,37 +160,6 @@ func (b *BaseApi) DeleteOllamaModel(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Load gpu / xpu info
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/gpu/load [get]
|
||||
func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
|
||||
ok, client := gpu.New()
|
||||
if ok {
|
||||
info, err := client.LoadGpuInfo()
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
return
|
||||
}
|
||||
xpuOK, xpuClient := xpu.New()
|
||||
if xpuOK {
|
||||
info, err := xpuClient.LoadGpuInfo()
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, &common.GpuInfo{})
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Bind domain
|
||||
// @Accept json
|
||||
|
||||
66
agent/app/api/v2/gpu.go
Normal file
66
agent/app/api/v2/gpu.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Load gpu / xpu info
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/gpu/load [get]
|
||||
func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
|
||||
ok, client := gpu.New()
|
||||
if ok {
|
||||
info, err := client.LoadGpuInfo()
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
return
|
||||
}
|
||||
xpuOK, xpuClient := xpu.New()
|
||||
if xpuOK {
|
||||
info, err := xpuClient.LoadGpuInfo()
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, &common.GpuInfo{})
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetCPUOptions(c *gin.Context) {
|
||||
helper.SuccessWithData(c, monitorService.LoadGPUOptions())
|
||||
}
|
||||
|
||||
// @Tags Monitor
|
||||
// @Summary Load monitor data
|
||||
// @Param request body dto.MonitorGPUSearch true "request"
|
||||
// @Success 200 {object} dto.MonitorGPUData
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/monitor/gpu/search [post]
|
||||
func (b *BaseApi) LoadGPUMonitor(c *gin.Context) {
|
||||
var req dto.MonitorGPUSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := monitorService.LoadGPUMonitorData(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
@@ -31,27 +31,6 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
helper.SuccessWithDataGzipped(c, data)
|
||||
}
|
||||
|
||||
// @Tags Monitor
|
||||
// @Summary Load monitor data
|
||||
// @Param request body dto.MonitorGPUSearch true "request"
|
||||
// @Success 200 {object} dto.MonitorGPUData
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/monitor/gpu/search [post]
|
||||
func (b *BaseApi) LoadGPUMonitor(c *gin.Context) {
|
||||
var req dto.MonitorGPUSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := monitorService.LoadGPUMonitorData(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Monitor
|
||||
// @Summary Clean monitor data
|
||||
// @Success 200
|
||||
@@ -126,7 +105,3 @@ func (b *BaseApi) GetIOOptions(c *gin.Context) {
|
||||
sort.Strings(options)
|
||||
helper.SuccessWithData(c, options)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetCPUOptions(c *gin.Context) {
|
||||
helper.SuccessWithData(c, monitorService.LoadGPUOptions())
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/service"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
@@ -19,117 +20,30 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (b *BaseApi) WsSSH(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
if global.CONF.Base.IsDemo {
|
||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
|
||||
hostID, _ := strconv.Atoi(c.DefaultQuery("id", "0"))
|
||||
var client *ssh.SSHClient
|
||||
if hostID > 0 {
|
||||
host, err := service.GetHostInfo(uint(hostID))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "load host info by id failed")) {
|
||||
return
|
||||
}
|
||||
connInfo := ssh.ConnInfo{
|
||||
Addr: host.Addr,
|
||||
Port: int(host.Port),
|
||||
User: host.User,
|
||||
AuthMode: host.AuthMode,
|
||||
Password: host.Password,
|
||||
PrivateKey: []byte(host.PrivateKey),
|
||||
}
|
||||
if len(host.PassPhrase) != 0 {
|
||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||
}
|
||||
client, err = ssh.NewClient(connInfo)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
client, err = loadLocalConn()
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
defer client.Close()
|
||||
command := c.DefaultQuery("command", "")
|
||||
sws, err := terminal.NewLogicSshWsSession(cols, rows, client.Client, wsConn, command)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer sws.Close()
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
sws.Start(quitChan)
|
||||
go sws.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
|
||||
dt := time.Now().Add(time.Second)
|
||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||
func (b *BaseApi) WsLocalTerminal(c *gin.Context) {
|
||||
client, err := loadLocalConn()
|
||||
b.runSSHSession(c, client, err, c.DefaultQuery("command", ""))
|
||||
}
|
||||
|
||||
func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
func (b *BaseApi) WsHostSSH(c *gin.Context) {
|
||||
hostID, _ := strconv.Atoi(c.DefaultQuery("id", "0"))
|
||||
if hostID <= 0 {
|
||||
b.runSSHSession(c, nil, errors.New("missing host id"), c.DefaultQuery("command", ""))
|
||||
return
|
||||
}
|
||||
host, err := service.GetHostInfo(uint(hostID))
|
||||
client, err := newHostSSHClient(host, err)
|
||||
b.runSSHSession(c, client, err, c.DefaultQuery("command", ""))
|
||||
}
|
||||
|
||||
func (b *BaseApi) WsContainerTerminal(c *gin.Context) {
|
||||
wsConn, cols, rows, ok := prepareTerminalSession(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
if global.CONF.Base.IsDemo {
|
||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
source := c.Query("source")
|
||||
var initCmd []string
|
||||
switch source {
|
||||
case "redis", "redis-cluster":
|
||||
initCmd, err = loadRedisInitCmd(c, source)
|
||||
case "ollama":
|
||||
initCmd, err = loadOllamaInitCmd(c)
|
||||
case "container":
|
||||
initCmd, err = loadContainerInitCmd(c)
|
||||
case "database":
|
||||
initCmd, err = loadDatabaseInitCmd(c)
|
||||
default:
|
||||
if wshandleError(wsConn, fmt.Errorf("not support such source %s", source)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
slave, err := terminal.NewCommand("docker", initCmd...)
|
||||
slave, err := loadContainerTerminalCommand(c)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
@@ -147,10 +61,107 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||
<-quitChan
|
||||
|
||||
global.LOG.Info("websocket finished")
|
||||
closeTerminalConn(wsConn)
|
||||
}
|
||||
|
||||
func prepareTerminalSession(c *gin.Context) (*websocket.Conn, int, int, bool) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||
return nil, 0, 0, false
|
||||
}
|
||||
|
||||
if global.CONF.Base.IsDemo {
|
||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
||||
return nil, 0, 0, false
|
||||
}
|
||||
}
|
||||
|
||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
||||
return nil, 0, 0, false
|
||||
}
|
||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return nil, 0, 0, false
|
||||
}
|
||||
return wsConn, cols, rows, true
|
||||
}
|
||||
|
||||
func (b *BaseApi) runSSHSession(c *gin.Context, client *ssh.SSHClient, clientErr error, command string) {
|
||||
wsConn, cols, rows, ok := prepareTerminalSession(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
if wshandleError(wsConn, errors.WithMessage(clientErr, "failed to set up the connection. Please check the host information")) {
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
sws, err := terminal.NewLogicSshWsSession(cols, rows, client.Client, wsConn, command)
|
||||
if wshandleError(wsConn, err) {
|
||||
return
|
||||
}
|
||||
defer sws.Close()
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
sws.Start(quitChan)
|
||||
go sws.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
|
||||
closeTerminalConn(wsConn)
|
||||
}
|
||||
|
||||
func closeTerminalConn(wsConn *websocket.Conn) {
|
||||
dt := time.Now().Add(time.Second)
|
||||
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
|
||||
}
|
||||
|
||||
func newHostSSHClient(host *model.Host, err error) (*ssh.SSHClient, error) {
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "load host info by id failed")
|
||||
}
|
||||
connInfo := ssh.ConnInfo{
|
||||
Addr: host.Addr,
|
||||
Port: int(host.Port),
|
||||
User: host.User,
|
||||
AuthMode: host.AuthMode,
|
||||
Password: host.Password,
|
||||
PrivateKey: []byte(host.PrivateKey),
|
||||
}
|
||||
if len(host.PassPhrase) != 0 {
|
||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||
}
|
||||
return ssh.NewClient(connInfo)
|
||||
}
|
||||
|
||||
func loadContainerTerminalCommand(c *gin.Context) (*terminal.LocalCommand, error) {
|
||||
source := c.Query("source")
|
||||
var (
|
||||
initCmd []string
|
||||
err error
|
||||
)
|
||||
switch source {
|
||||
case "redis", "redis-cluster":
|
||||
initCmd, err = loadRedisInitCmd(c, source)
|
||||
case "ollama":
|
||||
initCmd, err = loadOllamaInitCmd(c)
|
||||
case "container":
|
||||
initCmd, err = loadContainerInitCmd(c)
|
||||
case "database":
|
||||
initCmd, err = loadDatabaseInitCmd(c)
|
||||
default:
|
||||
return nil, fmt.Errorf("not support such source %s", source)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return terminal.NewCommand("docker", initCmd...)
|
||||
}
|
||||
|
||||
func loadRedisInitCmd(c *gin.Context, redisType string) ([]string, error) {
|
||||
name := c.Query("name")
|
||||
from := c.Query("from")
|
||||
|
||||
@@ -127,7 +127,7 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
||||
if installs, _ := appInstallRepo.ListBy(context.Background(), repo.WithByLowerName(req.Name)); len(installs) > 0 {
|
||||
return nil, buserr.New("ErrNameIsExist")
|
||||
}
|
||||
if !xpack.IsXpack() {
|
||||
if !xpack.MultiNodeProvider.IsXpack() {
|
||||
count, _, err := agentRepo.Page(1, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -501,7 +501,7 @@ func (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {
|
||||
Body: i18n.GetMsgByKey("TestAlert"),
|
||||
IsHTML: false,
|
||||
}
|
||||
transport := xpack.LoadRequestTransport()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
if err := email.SendMail(cfg, msg, transport); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -526,7 +526,7 @@ func loadSSHLogin(alert dto.AlertDTO) {
|
||||
|
||||
func loadNodeException(alert dto.AlertDTO) {
|
||||
// only master alert
|
||||
failCount, err := xpack.GetNodeErrorAlert()
|
||||
failCount, err := xpack.AlertProvider.GetNodeErrorAlert()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting node, err: %s", err)
|
||||
return
|
||||
@@ -555,7 +555,7 @@ func loadNodeException(alert dto.AlertDTO) {
|
||||
|
||||
func loadLicenseException(alert dto.AlertDTO) {
|
||||
// only master alert
|
||||
failCount, err := xpack.GetLicenseErrorAlert()
|
||||
failCount, err := xpack.AlertProvider.GetLicenseErrorAlert()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("error getting license, err: %s", err)
|
||||
return
|
||||
@@ -604,7 +604,7 @@ func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params [
|
||||
AlertId: alert.ID,
|
||||
Count: todayCount + 1,
|
||||
}
|
||||
alertErr := xpack.CreateSMSAlertLog(alertType, alert, create, quotaType, params, constant.SMS)
|
||||
alertErr := xpack.AlertProvider.CreateSMSAlertLog(alertType, alert, create, quotaType, params, constant.SMS)
|
||||
if alertErr != nil {
|
||||
global.LOG.Infof("%s alert sms push faild, err: %v", alertType, alertErr.Error())
|
||||
continue
|
||||
@@ -624,8 +624,8 @@ func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params [
|
||||
alertInfo.Type = alertType
|
||||
create.AlertRule = alertUtil.ProcessAlertRule(alert)
|
||||
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, constant.Email)
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
alertErr := alertUtil.CreateEmailAlertLog(create, alertInfo, params, transport, agentInfo)
|
||||
if alertErr != nil {
|
||||
global.LOG.Infof("%s alert email push faild, err: %v", alertType, alertErr.Error())
|
||||
@@ -642,9 +642,9 @@ func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params [
|
||||
AlertId: alert.ID,
|
||||
Count: todayCount + 1,
|
||||
}
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
err := xpack.CreateWebhookAlertLog(alertType, alert, create, quotaType, params, m, transport, agentInfo)
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
err := xpack.AlertProvider.CreateWebhookAlertLog(alertType, alert, create, quotaType, params, m, transport, agentInfo)
|
||||
if err != nil {
|
||||
global.LOG.Infof("%s alert webhook %s push faild, err: %v", alertType, m, err)
|
||||
continue
|
||||
@@ -664,8 +664,8 @@ func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params [
|
||||
alertInfo.Type = alertType
|
||||
create.AlertRule = alertUtil.ProcessAlertRule(alert)
|
||||
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, m)
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
alertErr := alertUtil.CreateBarkAlertLog(create, alertInfo, params, transport, agentInfo)
|
||||
if alertErr != nil {
|
||||
global.LOG.Infof("%s alert %s push failed, err: %v", alertType, m, alertErr.Error())
|
||||
|
||||
@@ -72,7 +72,7 @@ func (s *AlertSender) sendSMS(quota string, params []dto.Param) {
|
||||
Type: s.alert.Type,
|
||||
}
|
||||
|
||||
err := xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS)
|
||||
err := xpack.AlertProvider.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("%s alert sms push failed: %v", s.alert.Type, err)
|
||||
return
|
||||
@@ -95,8 +95,8 @@ func (s *AlertSender) sendEmail(quota string, params []dto.Param) {
|
||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
|
||||
}
|
||||
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("%s alert email push failed: %v", s.alert.Type, err)
|
||||
@@ -120,8 +120,8 @@ func (s *AlertSender) sendBark(quota string, params []dto.Param) {
|
||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
|
||||
}
|
||||
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("%s alert bark push failed: %v", s.alert.Type, err)
|
||||
@@ -142,9 +142,9 @@ func (s *AlertSender) sendWebhook(quota string, params []dto.Param, method strin
|
||||
AlertId: s.alert.ID,
|
||||
Type: s.alert.Type,
|
||||
}
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
err := xpack.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, method, transport, agentInfo)
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
err := xpack.AlertProvider.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, method, transport, agentInfo)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("%s alert %s webhook push failed: %v", s.alert.Type, method, err)
|
||||
return
|
||||
@@ -169,7 +169,7 @@ func (s *AlertSender) sendResourceSMS(quota string, params []dto.Param) {
|
||||
Type: s.alert.Type,
|
||||
}
|
||||
|
||||
if err := xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS); err != nil {
|
||||
if err := xpack.AlertProvider.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS); err != nil {
|
||||
global.LOG.Errorf("failed to send SMS alert: %v", err)
|
||||
return
|
||||
}
|
||||
@@ -191,8 +191,8 @@ func (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {
|
||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
|
||||
}
|
||||
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
if err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo); err != nil {
|
||||
global.LOG.Errorf("failed to send Email alert: %v", err)
|
||||
return
|
||||
@@ -215,8 +215,8 @@ func (s *AlertSender) sendResourceBark(quota string, params []dto.Param) {
|
||||
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
|
||||
}
|
||||
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
if err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo); err != nil {
|
||||
global.LOG.Errorf("failed to send Bark alert: %v", err)
|
||||
return
|
||||
@@ -236,9 +236,9 @@ func (s *AlertSender) sendResourceWebhook(quota string, params []dto.Param, meth
|
||||
AlertId: s.alert.ID,
|
||||
Type: s.alert.Type,
|
||||
}
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
if err := xpack.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, method, transport, agentInfo); err != nil {
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
if err := xpack.AlertProvider.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, method, transport, agentInfo); err != nil {
|
||||
global.LOG.Errorf("%s alert %s webhook push failed: %v", s.alert.Type, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -966,7 +966,7 @@ func deleteCustomApp() {
|
||||
}
|
||||
|
||||
func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||
if xpack.IsUseCustomApp() {
|
||||
if xpack.MultiNodeProvider.IsUseCustomApp() {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ func (a AppService) createSyncAppStoreTask(sharedCtx **appSyncContext) func(t *t
|
||||
|
||||
ctx := &appSyncContext{
|
||||
task: t,
|
||||
httpClient: http.Client{Timeout: time.Duration(constant.TimeOut20s) * time.Second, Transport: xpack.LoadRequestTransport()},
|
||||
httpClient: http.Client{Timeout: time.Duration(constant.TimeOut20s) * time.Second, Transport: xpack.MultiNodeProvider.LoadRequestTransport()},
|
||||
baseRemoteUrl: fmt.Sprintf("%s/%s/1panel", global.AppRepoURL(), global.CONF.Base.Mode),
|
||||
systemVersion: setting.SystemVersion,
|
||||
settingService: settingService,
|
||||
@@ -279,9 +279,9 @@ func (c *appSyncContext) syncAppIconsAndDetails() error {
|
||||
}()
|
||||
|
||||
var (
|
||||
completed int
|
||||
icon200Count int
|
||||
icon304Count int
|
||||
completed int
|
||||
icon200Count int
|
||||
icon304Count int
|
||||
iconFailCount int
|
||||
)
|
||||
milestones := [4]int{totalWork / 4, totalWork / 2, totalWork * 3 / 4, totalWork}
|
||||
|
||||
@@ -1178,7 +1178,7 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
imagePrefix := xpack.GetImagePrefix()
|
||||
imagePrefix := xpack.MultiNodeProvider.GetImagePrefix()
|
||||
dockerCLi, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1778,7 +1778,7 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
|
||||
if !serviceValid {
|
||||
return buserr.New("ErrFileParse")
|
||||
}
|
||||
imagePreFix := xpack.GetImagePrefix()
|
||||
imagePreFix := xpack.MultiNodeProvider.GetImagePrefix()
|
||||
if imagePreFix != "" {
|
||||
for _, service := range services {
|
||||
serviceValue := service.(map[string]interface{})
|
||||
|
||||
@@ -179,7 +179,7 @@ func (c *ClamService) Create(req dto.ClamCreate) error {
|
||||
clam.InfectedDir = ""
|
||||
}
|
||||
if len(req.Spec) != 0 {
|
||||
entryID, err := xpack.StartClam(&clam, false)
|
||||
entryID, err := xpack.MultiNodeProvider.StartClam(&clam, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -232,7 +232,7 @@ func (c *ClamService) Update(req dto.ClamUpdate) error {
|
||||
upMap["entry_id"] = 0
|
||||
}
|
||||
if len(req.Spec) != 0 && clam.Status != constant.StatusDisable {
|
||||
newEntryID, err := xpack.StartClam(&clamItem, true)
|
||||
newEntryID, err := xpack.MultiNodeProvider.StartClam(&clamItem, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -276,7 +276,7 @@ func (c *ClamService) UpdateStatus(id uint, status string) error {
|
||||
err error
|
||||
)
|
||||
if status == constant.StatusEnable {
|
||||
entryID, err = xpack.StartClam(&clam, true)
|
||||
entryID, err = xpack.MultiNodeProvider.StartClam(&clam, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -39,12 +39,13 @@ var monitorCancel context.CancelFunc
|
||||
type IMonitorService interface {
|
||||
Run()
|
||||
LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorData, error)
|
||||
LoadGPUOptions() dto.MonitorGPUOptions
|
||||
LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error)
|
||||
LoadSetting() (*dto.MonitorSetting, error)
|
||||
UpdateSetting(key, value string) error
|
||||
CleanData() error
|
||||
|
||||
LoadGPUOptions() dto.MonitorGPUOptions
|
||||
LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error)
|
||||
|
||||
saveIODataToDB(ctx context.Context, interval float64)
|
||||
saveNetDataToDB(ctx context.Context, interval float64)
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
|
||||
reloadSystemSSL(websiteSSL, logger)
|
||||
if websiteSSL.PushNode {
|
||||
printSSLLog(logger, "StartPushSSLToNode", nil)
|
||||
if err = xpack.PushSSLToNode(websiteSSL); err != nil {
|
||||
if err = xpack.MultiNodeProvider.PushSSLToNode(websiteSSL); err != nil {
|
||||
printSSLLog(logger, "PushSSLToNodeFailed", map[string]interface{}{"err": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -576,7 +576,7 @@ func delNginxConfig(website model.Website, force bool) error {
|
||||
}
|
||||
sitePath := GetSiteDir(website.Alias)
|
||||
if fileOp.Stat(sitePath) {
|
||||
xpack.RemoveTamper(website.Alias)
|
||||
xpack.MultiNodeProvider.RemoveTamper(website.Alias)
|
||||
_ = fileOp.DeleteDir(sitePath)
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,6 @@ require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/BurntSushi/toml v1.6.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.16 // indirect
|
||||
@@ -221,7 +220,6 @@ require (
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/sigstore/sigstore v1.10.5 // indirect
|
||||
github.com/sigstore/sigstore-go v1.1.4 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.80 // indirect
|
||||
|
||||
10
agent/go.sum
10
agent/go.sum
@@ -55,8 +55,6 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/sarama v1.30.1/go.mod h1:hGgx05L/DiW8XYBXeJdKIN6V2QUy2H6JqME5VT1NLRw=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
@@ -348,8 +346,6 @@ github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gammazero/toposort v0.1.1 h1:OivGxsWxF3U3+U80VoLJ+f50HcPU1MIqE1JlKzoJ2Eg=
|
||||
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
|
||||
github.com/gammazero/toposort v0.2.0 h1:4wndQUKr24ALbkDnYag1bMGJBR+2y++cIGST1lb+L9w=
|
||||
github.com/gammazero/toposort v0.2.0/go.mod h1:X4xn3isrX0SRJi+T0wfUkmzubOm2uOQynDzAsO4yiMQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko=
|
||||
github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
|
||||
@@ -727,8 +723,6 @@ github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04
|
||||
github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.9 h1:EZgAsW6DsuawxDgTtIdjCUBa2TQ6AOe9pnCidofSRtE=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.9/go.mod h1:5D3uct315OMkMRXKwEuMB+wQi/2m5NQngKDmApqwVlo=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
@@ -921,8 +915,6 @@ github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7D
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/qiniu/go-sdk/v7 v7.26.4 h1:D/k6cPbhjKvSx9hVBQSh97GuPu1ZtM3OLPCclSpusjo=
|
||||
github.com/qiniu/go-sdk/v7 v7.26.4/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk=
|
||||
github.com/qiniu/go-sdk/v7 v7.26.9 h1:is9th0m6RCq9LBq7Cuh7mmGUTfR2xb68SGO3xH7ZrA0=
|
||||
github.com/qiniu/go-sdk/v7 v7.26.9/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
@@ -980,8 +972,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
|
||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg=
|
||||
github.com/spdx/tools-golang v0.5.7/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw=
|
||||
|
||||
@@ -38,7 +38,7 @@ func initGlobalData() {
|
||||
if err := settingRepo.Update("SystemStatus", "Free"); err != nil {
|
||||
global.LOG.Fatalf("init service before start failed, err: %v", err)
|
||||
}
|
||||
node, _ := xpack.LoadNodeInfo(false)
|
||||
node, _ := xpack.MultiNodeProvider.LoadNodeInfo(false)
|
||||
if len(node.Version) != 0 {
|
||||
_ = settingRepo.Update("SystemVersion", node.Version)
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ var InitSetting = &gormigrate.Migration{
|
||||
ID: "20240722-init-setting",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
global.CONF.Base.EncryptKey = common.RandStr(16)
|
||||
nodeInfo, err := xpack.LoadNodeInfo(true)
|
||||
nodeInfo, err := xpack.MultiNodeProvider.LoadNodeInfo(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func Init() {
|
||||
}
|
||||
|
||||
func initBaseInfo() {
|
||||
nodeInfo, err := xpack.LoadNodeInfo(true)
|
||||
nodeInfo, err := xpack.MultiNodeProvider.LoadNodeInfo(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ func Certificate() gin.HandlerFunc {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if !xpack.ValidateCertificate(c) {
|
||||
if !xpack.MultiNodeProvider.ValidateCertificate(c) {
|
||||
CloseDirectly(c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//go:build xpack
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
xpackRouter "github.com/1Panel-dev/1Panel/agent/xpack/router"
|
||||
)
|
||||
|
||||
func RouterGroups() []CommonRouter {
|
||||
baseRouter := commonGroups()
|
||||
for _, ro := range xpackRouter.XpackGroups() {
|
||||
if val, ok := ro.(CommonRouter); ok {
|
||||
baseRouter = append(baseRouter, val)
|
||||
}
|
||||
}
|
||||
return baseRouter
|
||||
}
|
||||
|
||||
var RouterGroupApp = RouterGroups()
|
||||
@@ -1,19 +0,0 @@
|
||||
//go:build xpackee
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
xpackRouter "github.com/1Panel-dev/1Panel/agent/xpack/router"
|
||||
)
|
||||
|
||||
func RouterGroups() []CommonRouter {
|
||||
baseRouter := commonGroups()
|
||||
for _, ro := range xpackRouter.XpackGroups() {
|
||||
if val, ok := ro.(CommonRouter); ok {
|
||||
baseRouter = append(baseRouter, val)
|
||||
}
|
||||
}
|
||||
return baseRouter
|
||||
}
|
||||
|
||||
var RouterGroupApp = RouterGroups()
|
||||
@@ -20,11 +20,14 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
aiToolsRouter.POST("/ollama/model/sync", baseApi.SyncOllamaModel)
|
||||
aiToolsRouter.POST("/ollama/model/load", baseApi.LoadOllamaModelDetail)
|
||||
aiToolsRouter.POST("/ollama/model/del", baseApi.DeleteOllamaModel)
|
||||
aiToolsRouter.GET("/gpu/load", baseApi.LoadGpuInfo)
|
||||
aiToolsRouter.POST("/domain/bind", baseApi.BindDomain)
|
||||
aiToolsRouter.POST("/domain/get", baseApi.GetBindDomain)
|
||||
aiToolsRouter.POST("/domain/update", baseApi.UpdateBindDomain)
|
||||
|
||||
aiToolsRouter.GET("/gpu/load", baseApi.LoadGpuInfo)
|
||||
aiToolsRouter.POST("/gpu/search", baseApi.LoadGPUMonitor)
|
||||
aiToolsRouter.GET("/gpu/options", baseApi.GetCPUOptions)
|
||||
|
||||
aiToolsRouter.POST("/mcp/search", baseApi.PageMcpServers)
|
||||
aiToolsRouter.POST("/mcp/server", baseApi.CreateMcpServer)
|
||||
aiToolsRouter.POST("/mcp/server/update", baseApi.UpdateMcpServer)
|
||||
|
||||
@@ -11,7 +11,6 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
baRouter := Router.Group("containers")
|
||||
baseApi := v2.ApiGroupApp.BaseApi
|
||||
{
|
||||
baRouter.GET("/exec", baseApi.ContainerWsSSH)
|
||||
baRouter.GET("/stats/:id", baseApi.ContainerStats)
|
||||
|
||||
baRouter.POST("", baseApi.ContainerCreate)
|
||||
|
||||
@@ -39,9 +39,7 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
hostRouter.POST("/firewall/filter/chain/status", baseApi.LoadChainStatus)
|
||||
|
||||
hostRouter.POST("/monitor/search", baseApi.LoadMonitor)
|
||||
hostRouter.POST("/monitor/gpu/search", baseApi.LoadGPUMonitor)
|
||||
hostRouter.POST("/monitor/clean", baseApi.CleanMonitor)
|
||||
hostRouter.GET("/monitor/gpuoptions", baseApi.GetCPUOptions)
|
||||
hostRouter.GET("/monitor/netoptions", baseApi.GetNetworkOptions)
|
||||
hostRouter.GET("/monitor/iooptions", baseApi.GetIOOptions)
|
||||
hostRouter.GET("/monitor/setting", baseApi.LoadMonitorSetting)
|
||||
@@ -69,7 +67,9 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
hostRouter.GET("/tool/supervisor/process", baseApi.GetProcess)
|
||||
hostRouter.POST("/tool/supervisor/process/file", baseApi.GetProcessFile)
|
||||
|
||||
hostRouter.GET("/terminal", baseApi.WsSSH)
|
||||
hostRouter.GET("/terminal/local", baseApi.WsLocalTerminal)
|
||||
hostRouter.GET("/terminal/ssh", baseApi.WsHostSSH)
|
||||
hostRouter.GET("/terminal/container", baseApi.WsContainerTerminal)
|
||||
|
||||
hostRouter.GET("/disks", baseApi.GetCompleteDiskInfo)
|
||||
hostRouter.POST("/disks/partition", baseApi.PartitionDisk)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build xpack
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
xpack "github.com/1Panel-dev/1Panel/agent/xpack"
|
||||
)
|
||||
|
||||
func InitOthers() {
|
||||
xpack.Init()
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build xpackee
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
xpack "github.com/1Panel-dev/1Panel/agent/xpack"
|
||||
)
|
||||
|
||||
func InitOthers() {
|
||||
xpack.Init()
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func PushAlert(pushAlert dto.PushAlert) error {
|
||||
AlertId: alert.ID,
|
||||
Count: todayCount + 1,
|
||||
}
|
||||
err = xpack.CreateTaskScanSMSAlertLog(alert, alert.Type, create, pushAlert, constant.SMS)
|
||||
err = xpack.AlertProvider.CreateTaskScanSMSAlertLog(alert, alert.Type, create, pushAlert, constant.SMS)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("%s alert sms push failed: %v", alert.Type, err)
|
||||
continue
|
||||
@@ -58,8 +58,8 @@ func PushAlert(pushAlert dto.PushAlert) error {
|
||||
AlertId: alert.ID,
|
||||
Count: todayCount + 1,
|
||||
}
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
err = alertUtil.CreateTaskScanEmailAlertLog(alert, create, pushAlert, constant.Email, transport, agentInfo)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("%s alert email push failed: %v", alert.Type, err)
|
||||
@@ -76,8 +76,8 @@ func PushAlert(pushAlert dto.PushAlert) error {
|
||||
AlertId: alert.ID,
|
||||
Count: todayCount + 1,
|
||||
}
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
params := alertUtil.CreateAlertParams(alertUtil.GetCronJobTypeName(pushAlert.Param))
|
||||
alertDetail := alertUtil.ProcessAlertDetail(alert, pushAlert.TaskName, params, constant.Bark)
|
||||
alertRule := alertUtil.ProcessAlertRule(alert)
|
||||
@@ -99,9 +99,9 @@ func PushAlert(pushAlert dto.PushAlert) error {
|
||||
AlertId: alert.ID,
|
||||
Count: todayCount + 1,
|
||||
}
|
||||
transport := xpack.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.GetAgentInfo()
|
||||
err = xpack.CreateTaskScanWebhookAlertLog(alert, alert.Type, create, pushAlert, m, transport, agentInfo)
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
err = xpack.AlertProvider.CreateTaskScanWebhookAlertLog(alert, alert.Type, create, pushAlert, m, transport, agentInfo)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("%s alert %s webhook push failed: %v", alert.Type, m, err)
|
||||
continue
|
||||
|
||||
@@ -47,7 +47,7 @@ func HandleGet(url string) (*http.Response, error) {
|
||||
}
|
||||
|
||||
func HandleRequest(url, method string, timeout int) (int, []byte, error) {
|
||||
transport := xpack.LoadRequestTransport()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
client := http.Client{Timeout: time.Duration(timeout) * time.Second, Transport: transport}
|
||||
return HandleRequestWithClient(&client, url, method, timeout)
|
||||
}
|
||||
@@ -130,7 +130,7 @@ func RequestFile(url, method string, timeout int) (io.ReadCloser, context.Cancel
|
||||
return
|
||||
}
|
||||
}()
|
||||
transport := xpack.LoadRequestTransport()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
client := http.Client{Timeout: time.Duration(timeout) * time.Second, Transport: transport}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||
request, err := http.NewRequestWithContext(ctx, method, url, nil)
|
||||
|
||||
@@ -2,92 +2,8 @@
|
||||
|
||||
package xpack
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
import "github.com/1Panel-dev/1Panel/agent/utils/xpack/helper"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
var AlertProvider = helper.NewIAlertProvider()
|
||||
|
||||
func RemoveTamper(website string) {}
|
||||
|
||||
func StartClam(startClam *model.Clam, isUpdate bool) (int, error) {
|
||||
return 0, buserr.New("ErrXpackNotFound")
|
||||
}
|
||||
|
||||
func LoadNodeInfo(isBase bool) (model.NodeInfo, error) {
|
||||
var info model.NodeInfo
|
||||
info.BaseDir = common.LoadParams("BASE_DIR")
|
||||
info.Version = common.LoadParams("ORIGINAL_VERSION")
|
||||
info.Scope = "master"
|
||||
global.IsMaster = true
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func GetImagePrefix() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func IsUseCustomApp() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func IsXpack() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func CreateTaskScanSMSAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateSMSAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateTaskScanWebhookAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateWebhookAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetLicenseErrorAlert() (uint, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func GetNodeErrorAlert() (uint, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func LoadRequestTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 60 * time.Second,
|
||||
KeepAlive: 60 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
ResponseHeaderTimeout: 10 * time.Second,
|
||||
IdleConnTimeout: 15 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateCertificate(c *gin.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func PushSSLToNode(websiteSSL *model.WebsiteSSL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAgentInfo() (*dto.AgentInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
var MultiNodeProvider = helper.NewIMultiNodeProvider()
|
||||
|
||||
38
agent/utils/xpack/helper/alert.go
Normal file
38
agent/utils/xpack/helper/alert.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack/providers"
|
||||
)
|
||||
|
||||
type alertHelper struct{}
|
||||
|
||||
func NewIAlertProvider() providers.AlertProvider {
|
||||
return &alertHelper{}
|
||||
}
|
||||
|
||||
func (a *alertHelper) CreateTaskScanSMSAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *alertHelper) CreateSMSAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *alertHelper) CreateTaskScanWebhookAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *alertHelper) CreateWebhookAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *alertHelper) GetLicenseErrorAlert() (uint, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (a *alertHelper) GetNodeErrorAlert() (uint, error) {
|
||||
return 0, nil
|
||||
}
|
||||
74
agent/utils/xpack/helper/multi_node.go
Normal file
74
agent/utils/xpack/helper/multi_node.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack/providers"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type multiNodeHelper struct{}
|
||||
|
||||
func NewIMultiNodeProvider() providers.MultiNodeProvider {
|
||||
return &multiNodeHelper{}
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) RemoveTamper(website string) {}
|
||||
|
||||
func (m *multiNodeHelper) StartClam(startClam *model.Clam, isUpdate bool) (int, error) {
|
||||
return 0, buserr.New("ErrXpackNotFound")
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) LoadNodeInfo(isBase bool) (model.NodeInfo, error) {
|
||||
var info model.NodeInfo
|
||||
info.BaseDir = common.LoadParams("BASE_DIR")
|
||||
info.Version = common.LoadParams("ORIGINAL_VERSION")
|
||||
info.Scope = "master"
|
||||
global.IsMaster = true
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) GetImagePrefix() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) IsUseCustomApp() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) IsXpack() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) LoadRequestTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 60 * time.Second,
|
||||
KeepAlive: 60 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
ResponseHeaderTimeout: 10 * time.Second,
|
||||
IdleConnTimeout: 15 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) ValidateCertificate(c *gin.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) PushSSLToNode(websiteSSL *model.WebsiteSSL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) GetAgentInfo() (*dto.AgentInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
17
agent/utils/xpack/providers/alert.go
Normal file
17
agent/utils/xpack/providers/alert.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
)
|
||||
|
||||
type AlertProvider interface {
|
||||
GetNodeErrorAlert() (uint, error)
|
||||
GetLicenseErrorAlert() (uint, error)
|
||||
|
||||
CreateTaskScanSMSAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string) error
|
||||
CreateSMSAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string) error
|
||||
CreateTaskScanWebhookAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error
|
||||
CreateWebhookAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error
|
||||
}
|
||||
23
agent/utils/xpack/providers/multi_node.go
Normal file
23
agent/utils/xpack/providers/multi_node.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type MultiNodeProvider interface {
|
||||
IsXpack() bool
|
||||
IsUseCustomApp() bool
|
||||
GetImagePrefix() string
|
||||
RemoveTamper(website string)
|
||||
StartClam(startClam *model.Clam, isUpdate bool) (int, error)
|
||||
LoadNodeInfo(isBase bool) (model.NodeInfo, error)
|
||||
|
||||
LoadRequestTransport() *http.Transport
|
||||
ValidateCertificate(c *gin.Context) bool
|
||||
PushSSLToNode(websiteSSL *model.WebsiteSSL) error
|
||||
GetAgentInfo() (*dto.AgentInfo, error)
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
//go:build xpack
|
||||
|
||||
package xpack
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
edition "github.com/1Panel-dev/1Panel/agent/xpack/edition"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RemoveTamper(website string) {
|
||||
edition.RemoveTamper(website)
|
||||
}
|
||||
|
||||
func StartClam(startClam *model.Clam, isUpdate bool) (int, error) {
|
||||
return edition.StartClam(startClam, isUpdate)
|
||||
}
|
||||
|
||||
func LoadNodeInfo(isBase bool) (model.NodeInfo, error) {
|
||||
return edition.LoadNodeInfo(isBase)
|
||||
}
|
||||
|
||||
func GetImagePrefix() string {
|
||||
return edition.GetImagePrefix()
|
||||
}
|
||||
|
||||
func IsUseCustomApp() bool {
|
||||
return edition.IsUseCustomApp()
|
||||
}
|
||||
|
||||
func IsXpack() bool {
|
||||
return edition.IsXpack()
|
||||
}
|
||||
|
||||
func CreateTaskScanSMSAlertLog(info dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string) error {
|
||||
return edition.CreateTaskScanSMSAlertLog(info, alertType, create, pushAlert, method)
|
||||
}
|
||||
|
||||
func CreateSMSAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string) error {
|
||||
return edition.CreateSMSAlertLog(alertType, info, create, project, params, method)
|
||||
}
|
||||
|
||||
func CreateTaskScanWebhookAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
|
||||
return edition.CreateTaskScanWebhookAlertLog(alert, alertType, create, pushAlert, method, transport, agentInfo)
|
||||
}
|
||||
|
||||
func CreateWebhookAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
|
||||
return edition.CreateWebhookAlertLog(alertType, info, create, project, params, method, transport, agentInfo)
|
||||
}
|
||||
|
||||
func GetLicenseErrorAlert() (uint, error) {
|
||||
return edition.GetLicenseErrorAlert()
|
||||
}
|
||||
|
||||
func GetNodeErrorAlert() (uint, error) {
|
||||
return edition.GetNodeErrorAlert()
|
||||
}
|
||||
|
||||
func LoadRequestTransport() *http.Transport { return edition.LoadRequestTransport() }
|
||||
|
||||
func ValidateCertificate(c *gin.Context) bool {
|
||||
return edition.ValidateCertificate(c)
|
||||
}
|
||||
|
||||
func PushSSLToNode(websiteSSL *model.WebsiteSSL) error {
|
||||
return edition.PushSSLToNode(websiteSSL)
|
||||
}
|
||||
|
||||
func GetAgentInfo() (*dto.AgentInfo, error) {
|
||||
return edition.GetAgentInfo()
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
//go:build xpackee
|
||||
|
||||
package xpack
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
edition "github.com/1Panel-dev/1Panel/agent/xpack/edition"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RemoveTamper(website string) {
|
||||
edition.RemoveTamper(website)
|
||||
}
|
||||
|
||||
func StartClam(startClam *model.Clam, isUpdate bool) (int, error) {
|
||||
return edition.StartClam(startClam, isUpdate)
|
||||
}
|
||||
|
||||
func LoadNodeInfo(isBase bool) (model.NodeInfo, error) {
|
||||
return edition.LoadNodeInfo(isBase)
|
||||
}
|
||||
|
||||
func GetImagePrefix() string {
|
||||
return edition.GetImagePrefix()
|
||||
}
|
||||
|
||||
func IsUseCustomApp() bool {
|
||||
return edition.IsUseCustomApp()
|
||||
}
|
||||
|
||||
func IsXpack() bool {
|
||||
return edition.IsXpack()
|
||||
}
|
||||
|
||||
func CreateTaskScanSMSAlertLog(info dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string) error {
|
||||
return edition.CreateTaskScanSMSAlertLog(info, alertType, create, pushAlert, method)
|
||||
}
|
||||
|
||||
func CreateSMSAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string) error {
|
||||
return edition.CreateSMSAlertLog(alertType, info, create, project, params, method)
|
||||
}
|
||||
|
||||
func CreateTaskScanWebhookAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
|
||||
return edition.CreateTaskScanWebhookAlertLog(alert, alertType, create, pushAlert, method, transport, agentInfo)
|
||||
}
|
||||
|
||||
func CreateWebhookAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
|
||||
return edition.CreateWebhookAlertLog(alertType, info, create, project, params, method, transport, agentInfo)
|
||||
}
|
||||
|
||||
func GetLicenseErrorAlert() (uint, error) {
|
||||
return edition.GetLicenseErrorAlert()
|
||||
}
|
||||
|
||||
func GetNodeErrorAlert() (uint, error) {
|
||||
return edition.GetNodeErrorAlert()
|
||||
}
|
||||
|
||||
func LoadRequestTransport() *http.Transport { return edition.LoadRequestTransport() }
|
||||
|
||||
func ValidateCertificate(c *gin.Context) bool {
|
||||
return edition.ValidateCertificate(c)
|
||||
}
|
||||
|
||||
func PushSSLToNode(websiteSSL *model.WebsiteSSL) error {
|
||||
return edition.PushSSLToNode(websiteSSL)
|
||||
}
|
||||
|
||||
func GetAgentInfo() (*dto.AgentInfo, error) {
|
||||
return edition.GetAgentInfo()
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
initauth "github.com/1Panel-dev/1Panel/core/init/auth"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/captcha"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -58,7 +59,7 @@ func (b *BaseApi) Login(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
user, msgKey, err := authService.Login(c, req, string(entrance))
|
||||
user, msgKey, err := xpack.AuthProvider.Login(c, req, string(entrance))
|
||||
if user == nil || user.MfaStatus != constant.StatusEnable {
|
||||
go saveLoginLogs(c, wrapLoginErr(msgKey, err))
|
||||
}
|
||||
@@ -106,7 +107,7 @@ func (b *BaseApi) MFALogin(c *gin.Context) {
|
||||
entrance, _ = base64.StdEncoding.DecodeString(entranceItem)
|
||||
}
|
||||
|
||||
user, msgKey, err := authService.MFALogin(c, req, string(entrance))
|
||||
user, msgKey, err := xpack.AuthProvider.MFALogin(c, req, string(entrance))
|
||||
go saveLoginLogs(c, wrapLoginErr(msgKey, err))
|
||||
if msgKey == "ErrMFA" {
|
||||
global.IPTracker.RecordFailure(ip)
|
||||
@@ -134,7 +135,7 @@ func (b *BaseApi) MFALogin(c *gin.Context) {
|
||||
// @Router /core/auth/passkey/begin [post]
|
||||
func (b *BaseApi) PasskeyBeginLogin(c *gin.Context) {
|
||||
entrance := loadEntranceFromRequest(c)
|
||||
res, msgKey, err := authService.PasskeyBeginLogin(c, entrance)
|
||||
res, msgKey, err := xpack.AuthProvider.PasskeyBeginLogin(c, entrance)
|
||||
if msgKey != "" {
|
||||
if msgKey == "ErrEntrance" {
|
||||
helper.BadAuth(c, msgKey, err)
|
||||
@@ -161,7 +162,7 @@ func (b *BaseApi) PasskeyBeginLogin(c *gin.Context) {
|
||||
func (b *BaseApi) PasskeyFinishLogin(c *gin.Context) {
|
||||
sessionID := c.GetHeader("Passkey-Session")
|
||||
entrance := loadEntranceFromRequest(c)
|
||||
user, msgKey, err := authService.PasskeyFinishLogin(c, sessionID, entrance)
|
||||
user, msgKey, err := xpack.AuthProvider.PasskeyFinishLogin(c, sessionID, entrance)
|
||||
go saveLoginLogs(c, wrapLoginErr(msgKey, err))
|
||||
if msgKey == "ErrAuth" || msgKey == "ErrEntrance" {
|
||||
if msgKey == "ErrAuth" {
|
||||
@@ -248,10 +249,218 @@ func (b *BaseApi) GetLoginSetting(c *gin.Context) {
|
||||
Theme: settingInfo.Theme,
|
||||
NeedCaptcha: needCaptcha,
|
||||
}
|
||||
res.PasskeySetting = authService.PasskeyStatus(c)
|
||||
res.PasskeySetting = xpack.AuthProvider.PasskeyStatus(c)
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Begin passkey registration
|
||||
// @Accept json
|
||||
// @Param request body dto.PasskeyRegisterRequest true "request"
|
||||
// @Success 200 {object} dto.PasskeyBeginResponse
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/passkey/register/begin [post]
|
||||
func (b *BaseApi) PasskeyRegisterBegin(c *gin.Context) {
|
||||
var req dto.PasskeyRegisterRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
res, msgKey, err := xpack.AuthProvider.PasskeyBeginRegister(c, req.Name)
|
||||
if msgKey != "" {
|
||||
helper.ErrorWithDetail(c, http.StatusBadRequest, msgKey, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Finish passkey registration
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/passkey/register/finish [post]
|
||||
func (b *BaseApi) PasskeyRegisterFinish(c *gin.Context) {
|
||||
sessionID := c.GetHeader("Passkey-Session")
|
||||
msgKey, err := xpack.AuthProvider.PasskeyFinishRegister(c, sessionID)
|
||||
if msgKey != "" {
|
||||
helper.ErrorWithDetail(c, http.StatusBadRequest, msgKey, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary List passkeys
|
||||
// @Success 200 {array} dto.PasskeyInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/passkey/list [get]
|
||||
func (b *BaseApi) PasskeyList(c *gin.Context) {
|
||||
list, err := xpack.AuthProvider.PasskeyList(c)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Delete passkey
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/passkey/del [post]
|
||||
func (b *BaseApi) PasskeyDelete(c *gin.Context) {
|
||||
var req dto.PasskeyID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := xpack.AuthProvider.PasskeyDelete(c, req.ID); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load mfa info
|
||||
// @Accept json
|
||||
// @Param request body dto.MfaCredential true "request"
|
||||
// @Success 200 {object} mfa.Otp
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/mfa [post]
|
||||
func (b *BaseApi) LoadMFA(c *gin.Context) {
|
||||
var req dto.MfaRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
otp, err := xpack.AuthProvider.LoadMFA(c, req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, otp)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Bind mfa
|
||||
// @Accept json
|
||||
// @Param request body dto.MfaCredential true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/mfa/bind [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"mfa 绑定","formatEN":"bind mfa"}
|
||||
func (b *BaseApi) MFABind(c *gin.Context) {
|
||||
var req dto.MfaCredential
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := xpack.AuthProvider.MFABind(c, req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary generate api key
|
||||
// @Accept json
|
||||
// @Success 200 {string} key
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/api/generate [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.BadAuth(c, "ErrApiConfigDisable", nil)
|
||||
return
|
||||
}
|
||||
apiKey, err := xpack.AuthProvider.GenerateApiKey(c)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, apiKey)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Update api config
|
||||
// @Accept json
|
||||
// @Param request body dto.ApiInterfaceConfig true "request"
|
||||
// @Success 200
|
||||
// @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]"}
|
||||
func (b *BaseApi) UpdateApiConfig(c *gin.Context) {
|
||||
panelToken := c.GetHeader("1Panel-Token")
|
||||
if panelToken != "" {
|
||||
helper.BadAuth(c, "ErrApiConfigDisable", nil)
|
||||
return
|
||||
}
|
||||
var req dto.ApiInterfaceConfig
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := xpack.AuthProvider.UpdateApiConfig(c, req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Load current user info
|
||||
// @Success 200 {object} dto.CurrentUserInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/current [get]
|
||||
func (b *BaseApi) GetCurrentUser(c *gin.Context) {
|
||||
userInfo, err := xpack.AuthProvider.GetCurrentUserInfo(c)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, userInfo)
|
||||
}
|
||||
|
||||
// @Tags Auth
|
||||
// @Summary Update current user info
|
||||
// @Accept json
|
||||
// @Param request body dto.CurrentUserUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/auth/current/update [post]
|
||||
func (b *BaseApi) UpdateCurrentUser(c *gin.Context) {
|
||||
var req dto.CurrentUserUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := xpack.AuthProvider.UpdateCurrentUserInfo(c, req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func saveLoginLogs(c *gin.Context, err error) {
|
||||
var logs model.LoginLog
|
||||
if err != nil {
|
||||
|
||||
@@ -186,7 +186,7 @@ func (b *BaseApi) RunScript(c *gin.Context) {
|
||||
tty.Start(quitChan)
|
||||
go slave.Wait(quitChan)
|
||||
} else {
|
||||
connInfo, _, err := xpack.LoadNodeInfo(currentNode)
|
||||
connInfo, _, err := xpack.MultiNodeProvider.LoadNodeInfo(currentNode)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@ import (
|
||||
"strings"
|
||||
|
||||
"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/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/app/service"
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/mfa"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -36,6 +36,21 @@ func (b *BaseApi) GetSettingInfo(c *gin.Context) {
|
||||
helper.SuccessWithData(c, setting)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load base system setting info
|
||||
// @Success 200 {object} dto.SettingBaseInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/settings/search/base [post]
|
||||
func (b *BaseApi) GetSettingBaseInfo(c *gin.Context) {
|
||||
setting, err := settingService.GetSettingBaseInfo()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, setting)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load system setting by key
|
||||
// @Success 200 {string} info
|
||||
@@ -114,7 +129,7 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
if req.Key == "SecurityEntrance" {
|
||||
service.SetSecurityEntranceCookie(c, req.Value)
|
||||
appauth.SetSecurityEntranceCookie(c, req.Value)
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
@@ -210,28 +225,6 @@ func (b *BaseApi) DefaultMenu(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system password
|
||||
// @Accept json
|
||||
// @Param request body dto.PasswordUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/settings/password/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改系统密码","formatEN":"update system password"}
|
||||
func (b *BaseApi) UpdatePassword(c *gin.Context) {
|
||||
var req dto.PasswordUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.UpdatePassword(c, req.OldPassword, req.NewPassword); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update system ssl
|
||||
// @Accept json
|
||||
@@ -360,149 +353,7 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.HandlePasswordExpired(c, req.OldPassword, req.NewPassword); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load mfa info
|
||||
// @Accept json
|
||||
// @Param request body dto.MfaCredential true "request"
|
||||
// @Success 200 {object} mfa.Otp
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/settings/mfa [post]
|
||||
func (b *BaseApi) LoadMFA(c *gin.Context) {
|
||||
var req dto.MfaRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
otp, err := mfa.GetOtp("admin", req.Title, req.Interval)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, otp)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Bind mfa
|
||||
// @Accept json
|
||||
// @Param request body dto.MfaCredential true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/settings/mfa/bind [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"mfa 绑定","formatEN":"bind mfa"}
|
||||
func (b *BaseApi) MFABind(c *gin.Context) {
|
||||
var req dto.MfaCredential
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
success := mfa.ValidCode(req.Code, req.Interval, req.Secret)
|
||||
if !success {
|
||||
helper.InternalServer(c, errors.New("code is not valid"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.Update(c, "MFAInterval", req.Interval); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.Update(c, "MFAStatus", constant.StatusEnable); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.Update(c, "MFASecret", req.Secret); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Begin passkey registration
|
||||
// @Accept json
|
||||
// @Param request body dto.PasskeyRegisterRequest true "request"
|
||||
// @Success 200 {object} dto.PasskeyBeginResponse
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/settings/passkey/register/begin [post]
|
||||
func (b *BaseApi) PasskeyRegisterBegin(c *gin.Context) {
|
||||
var req dto.PasskeyRegisterRequest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
res, msgKey, err := authService.PasskeyBeginRegister(c, req.Name)
|
||||
if msgKey != "" {
|
||||
helper.ErrorWithDetail(c, http.StatusBadRequest, msgKey, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Finish passkey registration
|
||||
// @Accept json
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/settings/passkey/register/finish [post]
|
||||
func (b *BaseApi) PasskeyRegisterFinish(c *gin.Context) {
|
||||
sessionID := c.GetHeader("Passkey-Session")
|
||||
msgKey, err := authService.PasskeyFinishRegister(c, sessionID)
|
||||
if msgKey != "" {
|
||||
helper.ErrorWithDetail(c, http.StatusBadRequest, msgKey, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary List passkeys
|
||||
// @Success 200 {array} dto.PasskeyInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/settings/passkey/list [get]
|
||||
func (b *BaseApi) PasskeyList(c *gin.Context) {
|
||||
list, err := authService.PasskeyList()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Delete passkey
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/settings/passkey/{id} [delete]
|
||||
func (b *BaseApi) PasskeyDelete(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
helper.BadRequest(c, errors.New("passkey id is required"))
|
||||
return
|
||||
}
|
||||
if err := authService.PasskeyDelete(id); err != nil {
|
||||
if err := xpack.AuthProvider.HandlePasswordExpired(c, req.OldPassword, req.NewPassword); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
@@ -522,55 +373,6 @@ func (b *BaseApi) ReloadSSL(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary generate api key
|
||||
// @Accept json
|
||||
// @Success 200 {string} key
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/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.BadAuth(c, "ErrApiConfigDisable", nil)
|
||||
return
|
||||
}
|
||||
apiKey, err := settingService.GenerateApiKey()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, apiKey)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Update api config
|
||||
// @Accept json
|
||||
// @Param request body dto.ApiInterfaceConfig true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /core/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.BadAuth(c, "ErrApiConfigDisable", nil)
|
||||
return
|
||||
}
|
||||
var req dto.ApiInterfaceConfig
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.UpdateApiConfig(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Update appstore config
|
||||
// @Accept json
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package middleware
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
@@ -9,13 +9,24 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func ApiAuth() gin.HandlerFunc {
|
||||
type APIAuthConfig struct {
|
||||
ApiInterfaceStatus string
|
||||
ApiKey string
|
||||
IpWhiteList string
|
||||
ApiKeyValidityTime string
|
||||
}
|
||||
|
||||
type APIAuthConfigLoader func(c *gin.Context) (APIAuthConfig, error)
|
||||
type APIAuthSuccessHandler func(c *gin.Context, config APIAuthConfig)
|
||||
|
||||
func APIAuthMiddleware(loadConfig APIAuthConfigLoader, onSuccess APIAuthSuccessHandler) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if strings.HasPrefix(c.Request.URL.Path, "/api/v2/core/auth") {
|
||||
c.Next()
|
||||
@@ -24,35 +35,61 @@ func ApiAuth() gin.HandlerFunc {
|
||||
|
||||
panelToken := c.GetHeader("1Panel-Token")
|
||||
panelTimestamp := c.GetHeader("1Panel-Timestamp")
|
||||
if panelToken != "" || panelTimestamp != "" {
|
||||
if global.Api.ApiInterfaceStatus == constant.StatusEnable {
|
||||
clientIP := c.ClientIP()
|
||||
if !isValid1PanelTimestamp(panelTimestamp) {
|
||||
helper.BadAuth(c, "ErrApiConfigKeyTimeInvalid", nil)
|
||||
return
|
||||
}
|
||||
if !isValid1PanelToken(panelToken, panelTimestamp) {
|
||||
helper.BadAuth(c, "ErrApiConfigKeyInvalid", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !isIPInWhiteList(clientIP) {
|
||||
helper.BadAuth(c, "ErrApiConfigIPInvalid", nil)
|
||||
return
|
||||
}
|
||||
c.Set("API_AUTH", true)
|
||||
c.Next()
|
||||
return
|
||||
} else {
|
||||
helper.BadAuth(c, "ErrApiConfigStatusInvalid", nil)
|
||||
return
|
||||
}
|
||||
if panelToken == "" && panelTimestamp == "" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
config, err := loadConfig(c)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
if config.ApiInterfaceStatus != constant.StatusEnable {
|
||||
helper.BadAuth(c, "ErrApiConfigStatusInvalid", nil)
|
||||
return
|
||||
}
|
||||
if !isValid1PanelTimestamp(panelTimestamp, config.ApiKeyValidityTime) {
|
||||
helper.BadAuth(c, "ErrApiConfigKeyTimeInvalid", nil)
|
||||
return
|
||||
}
|
||||
if !isValid1PanelToken(panelToken, panelTimestamp, config.ApiKey) {
|
||||
helper.BadAuth(c, "ErrApiConfigKeyInvalid", nil)
|
||||
return
|
||||
}
|
||||
if !isIPInWhiteList(c.ClientIP(), config.IpWhiteList) {
|
||||
helper.BadAuth(c, "ErrApiConfigIPInvalid", nil)
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("API_AUTH", true)
|
||||
if onSuccess != nil {
|
||||
onSuccess(c, config)
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func isValid1PanelTimestamp(panelTimestamp string) bool {
|
||||
apiKeyValidityTime := global.Api.ApiKeyValidityTime
|
||||
func LoadAPIAuthConfig(_ *gin.Context) (APIAuthConfig, error) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
config := APIAuthConfig{}
|
||||
var err error
|
||||
if config.ApiInterfaceStatus, err = settingRepo.GetValueByKey("ApiInterfaceStatus"); err != nil {
|
||||
return config, err
|
||||
}
|
||||
if config.ApiKey, err = settingRepo.GetValueByKey("ApiKey"); err != nil {
|
||||
return config, err
|
||||
}
|
||||
if config.IpWhiteList, err = settingRepo.GetValueByKey("IpWhiteList"); err != nil {
|
||||
return config, err
|
||||
}
|
||||
if config.ApiKeyValidityTime, err = settingRepo.GetValueByKey("ApiKeyValidityTime"); err != nil {
|
||||
return config, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func isValid1PanelTimestamp(panelTimestamp string, apiKeyValidityTime string) bool {
|
||||
apiTime, err := strconv.Atoi(apiKeyValidityTime)
|
||||
if err != nil || apiTime < 0 {
|
||||
global.LOG.Errorf("apiTime %d, err: %v", apiTime, err)
|
||||
@@ -75,13 +112,11 @@ func isValid1PanelTimestamp(panelTimestamp string) bool {
|
||||
return nowTime-panelTime <= int64(apiTime)*60+tolerance
|
||||
}
|
||||
|
||||
func isValid1PanelToken(panelToken string, panelTimestamp string) bool {
|
||||
system1PanelToken := global.Api.ApiKey
|
||||
return panelToken == GenerateMD5("1panel"+system1PanelToken+panelTimestamp)
|
||||
func isValid1PanelToken(panelToken string, panelTimestamp string, apiKey string) bool {
|
||||
return panelToken == GenerateMD5("1panel"+apiKey+panelTimestamp)
|
||||
}
|
||||
|
||||
func isIPInWhiteList(clientIP string) bool {
|
||||
ipWhiteString := global.Api.IpWhiteList
|
||||
func isIPInWhiteList(clientIP string, ipWhiteString string) bool {
|
||||
if len(ipWhiteString) == 0 {
|
||||
global.LOG.Error("IP whitelist is empty")
|
||||
return false
|
||||
372
core/app/auth/auth.go
Normal file
372
core/app/auth/auth.go
Normal file
@@ -0,0 +1,372 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
initauth "github.com/1Panel-dev/1Panel/core/init/auth"
|
||||
"github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/mfa"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
nameSetting, err := settingRepo.Get(repo.WithByKey("UserName"))
|
||||
if err != nil {
|
||||
return nil, "", buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if info.Name != nameSetting.Value {
|
||||
return nil, "ErrAuth", buserr.New("ErrAuth")
|
||||
}
|
||||
priKey, _ := settingRepo.Get(repo.WithByKey("PASSWORD_PRIVATE_KEY"))
|
||||
passwordSetting, err := settingRepo.Get(repo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err = CheckPassword(priKey.Value, info.Password, passwordSetting.Value); err != nil {
|
||||
return nil, "ErrAuth", err
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
|
||||
return nil, "ErrEntrance", buserr.New("ErrEntrance")
|
||||
}
|
||||
mfaSetting, err := settingRepo.Get(repo.WithByKey("MFAStatus"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err = settingRepo.Update("Language", info.Language); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if mfaSetting.Value == constant.StatusEnable {
|
||||
return BeginMFALogin(c, nameSetting.Value, entrance, mfaSetting.Value), "", nil
|
||||
}
|
||||
|
||||
sessionUser := psession.SessionUser{ID: psession.SuperAdminSessionUserID, Name: nameSetting.Value, Role: "ADMIN"}
|
||||
res, err := GenerateSession(c, sessionUser)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if entrance != "" {
|
||||
SetSecurityEntranceCookie(c, entrance)
|
||||
}
|
||||
return res, "", nil
|
||||
}
|
||||
|
||||
func MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error) {
|
||||
name, errCode, err := VerifyMFALogin(c, info.SessionID, info.Code, entrance)
|
||||
if errCode != "" {
|
||||
return nil, errCode, err
|
||||
}
|
||||
|
||||
sessionUser := psession.SessionUser{ID: psession.SuperAdminSessionUserID, Name: name, Role: "ADMIN"}
|
||||
res, err := GenerateSession(c, sessionUser)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if entrance != "" {
|
||||
SetSecurityEntranceCookie(c, entrance)
|
||||
}
|
||||
return res, "", nil
|
||||
}
|
||||
|
||||
func BeginMFALogin(c *gin.Context, name, entrance, mfaStatus string) *dto.UserLoginInfo {
|
||||
ip := common.GetRealClientIP(c)
|
||||
mfaSession := initauth.GetMFASessionStore().Set(name, entrance, ip)
|
||||
return &dto.UserLoginInfo{Name: name, MfaStatus: mfaStatus, MfaSession: mfaSession}
|
||||
}
|
||||
|
||||
func VerifyMFALogin(c *gin.Context, sessionID, code, entrance string) (string, string, error) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
mfaSessions := initauth.GetMFASessionStore()
|
||||
session, ok := mfaSessions.Get(sessionID)
|
||||
if !ok {
|
||||
return "", "ErrMFA", nil
|
||||
}
|
||||
if session.IP != common.GetRealClientIP(c) {
|
||||
return "", "ErrMFA", nil
|
||||
}
|
||||
if session.Entrance != entrance {
|
||||
return "", "", buserr.New("ErrEntrance")
|
||||
}
|
||||
mfaSecret, err := settingRepo.Get(repo.WithByKey("MFASecret"))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
mfaInterval, err := settingRepo.Get(repo.WithByKey("MFAInterval"))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if !mfa.ValidCode(code, mfaInterval.Value, mfaSecret.Value) {
|
||||
return "", "ErrMFA", nil
|
||||
}
|
||||
mfaSessions.Delete(sessionID)
|
||||
return session.Name, "", nil
|
||||
}
|
||||
|
||||
func GenerateSession(c *gin.Context, sessionUser psession.SessionUser) (*dto.UserLoginInfo, error) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
setting, err := settingRepo.Get(repo.WithByKey("SessionTimeout"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpsSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lifeTime, err := strconv.Atoi(setting.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := global.SESSION.SetFresh(c, sessionUser, httpsSetting.Value == constant.StatusEnable, lifeTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.UserLoginInfo{Name: sessionUser.Name, Role: sessionUser.Role}, nil
|
||||
}
|
||||
|
||||
func SetSecurityEntranceCookie(c *gin.Context, entrance string) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
entranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))
|
||||
sslEnabled := false
|
||||
if setting, err := settingRepo.Get(repo.WithByKey("SSL")); err == nil {
|
||||
sslEnabled = setting.Value == constant.StatusEnable
|
||||
}
|
||||
c.SetCookie("SecurityEntrance", entranceValue, 0, "/", "", sslEnabled, true)
|
||||
}
|
||||
|
||||
func CheckEntrance(entrance string) error {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
|
||||
return buserr.New("ErrEntrance")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckPassword(priKey, password, passwordFromDB string) error {
|
||||
privateKey, err := encrypt.ParseRSAPrivateKey(priKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loginPassword, err := encrypt.DecryptPassword(password, privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
existPassword, err := encrypt.StringDecrypt(passwordFromDB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !hmac.Equal([]byte(loginPassword), []byte(existPassword)) {
|
||||
return buserr.New("ErrAuth")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadMFA(req dto.MfaRequest) (mfa.Otp, error) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
username, err := settingRepo.GetValueByKey("UserName")
|
||||
if err != nil {
|
||||
return mfa.Otp{}, err
|
||||
}
|
||||
otp, err := mfa.GetOtp(username, req.Title, req.Interval)
|
||||
if err != nil {
|
||||
return mfa.Otp{}, err
|
||||
}
|
||||
return otp, nil
|
||||
}
|
||||
func MFABind(req dto.MfaCredential) error {
|
||||
success := mfa.ValidCode(req.Code, req.Interval, req.Secret)
|
||||
if !success {
|
||||
return errors.New("code is not valid")
|
||||
}
|
||||
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
if err := settingRepo.Update("MFAInterval", req.Interval); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("MFAStatus", constant.StatusEnable); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("MFASecret", req.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func GetCurrentUserInfo() (*dto.CurrentUserInfo, error) {
|
||||
setting, err := repo.NewISettingRepo().List()
|
||||
if err != nil {
|
||||
return nil, buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
settingMap := make(map[string]string)
|
||||
for _, set := range setting {
|
||||
settingMap[set.Key] = set.Value
|
||||
}
|
||||
var info dto.CurrentUserInfo
|
||||
stringSettingMap := make(map[string]string, len(settingMap))
|
||||
for key, value := range settingMap {
|
||||
stringSettingMap[key] = value
|
||||
}
|
||||
delete(stringSettingMap, "SessionTimeout")
|
||||
delete(stringSettingMap, "ExpirationDays")
|
||||
arr, err := json.Marshal(stringSettingMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(arr, &info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info.SessionTimeout, _ = strconv.Atoi(settingMap["SessionTimeout"])
|
||||
info.ExpirationDays, _ = strconv.Atoi(settingMap["ExpirationDays"])
|
||||
info.Name = settingMap["UserName"]
|
||||
return &info, nil
|
||||
}
|
||||
func UpdateCurrentUserInfo(c *gin.Context, req dto.CurrentUserUpdate) error {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
if len(req.Password) != 0 {
|
||||
if len(req.OldPassword) == 0 {
|
||||
return buserr.New("ErrInitialPassword")
|
||||
}
|
||||
oldPassword, err := base64.StdEncoding.DecodeString(req.OldPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newPassword, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := HandlePasswordExpired(c, string(oldPassword), string(newPassword)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := settingRepo.Update("UserName", req.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("SessionTimeout", strconv.Itoa(req.SessionTimeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("ExpirationDays", strconv.Itoa(req.ExpirationDays)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, req.ExpirationDays).Format(constant.DateTimeLayout)); err != nil {
|
||||
return err
|
||||
}
|
||||
deleteCurrentSession(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateApiKey() (string, error) {
|
||||
apiKey := common.RandStr(32)
|
||||
if err := repo.NewISettingRepo().Update("ApiKey", apiKey); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return apiKey, nil
|
||||
}
|
||||
func UpdateApiConfig(req dto.ApiInterfaceConfig) error {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
if err := settingRepo.UpdateOrCreate("ApiInterfaceStatus", req.ApiInterfaceStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("ApiKey", req.ApiKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("IpWhiteList", req.IpWhiteList); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("ApiKeyValidityTime", req.ApiKeyValidityTime); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandlePasswordExpired(c *gin.Context, old, new string) error {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
setting, err := settingRepo.Get(repo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordFromDB, err := encrypt.StringDecrypt(setting.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if passwordFromDB == old {
|
||||
newPassword, err := encrypt.StringEncrypt(new)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("Password", newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expiredSetting, err := settingRepo.Get(repo.WithByKey("ExpirationDays"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeout, _ := strconv.Atoi(expiredSetting.Value)
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format(constant.DateTimeLayout)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return buserr.New("ErrInitialPassword")
|
||||
}
|
||||
|
||||
func LoadSessionTimeout(sessionUser psession.SessionUser) (int, error) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
sessionTimeout, err := settingRepo.GetValueByKey("SessionTimeout")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
lifeTime, _ := strconv.Atoi(sessionTimeout)
|
||||
return lifeTime, nil
|
||||
}
|
||||
func LoadExpired(sessionUser psession.SessionUser) (bool, time.Time, error) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
expirationDays, err := settingRepo.GetValueByKey("ExpirationDays")
|
||||
if err != nil {
|
||||
return true, time.Time{}, err
|
||||
}
|
||||
expiredDays, _ := strconv.Atoi(expirationDays)
|
||||
if expiredDays == 0 {
|
||||
return false, time.Time{}, nil
|
||||
}
|
||||
|
||||
expirationTime, err := settingRepo.GetValueByKey("ExpirationTime")
|
||||
if err != nil {
|
||||
return true, time.Time{}, err
|
||||
}
|
||||
expiredTime, err := time.ParseInLocation(constant.DateTimeLayout, expirationTime, common.LoadExpiredLocation())
|
||||
if err != nil {
|
||||
return true, time.Time{}, err
|
||||
}
|
||||
return true, expiredTime, nil
|
||||
}
|
||||
|
||||
func deleteCurrentSession(c *gin.Context) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
sessionUser, err := global.SESSION.Get(c)
|
||||
if err != nil || sessionUser.ID == "" {
|
||||
return
|
||||
}
|
||||
_ = global.SESSION.DeleteByID(sessionUser.ID)
|
||||
}
|
||||
598
core/app/auth/passkey.go
Normal file
598
core/app/auth/passkey.go
Normal file
@@ -0,0 +1,598 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/passkey"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func EvaluatePasskeyStatus(c *gin.Context, configured func() (bool, error)) bool {
|
||||
enabled, err := PasskeyEnabled(c)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("passkey enabled check failed, err: %v", err)
|
||||
enabled = false
|
||||
}
|
||||
configuredOK, err := configured()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("passkey config check failed, err: %v", err)
|
||||
configuredOK = false
|
||||
}
|
||||
return enabled && configuredOK
|
||||
}
|
||||
|
||||
func PasskeyStatus(c *gin.Context) bool {
|
||||
return EvaluatePasskeyStatus(c, communityPasskeyConfigured)
|
||||
}
|
||||
|
||||
func PasskeyBeginLogin(c *gin.Context, entrance string) (*dto.PasskeyBeginResponse, string, error) {
|
||||
if err := CheckEntrance(entrance); err != nil {
|
||||
return nil, "ErrEntrance", err
|
||||
}
|
||||
config, msgKey, err := PasskeyConfig(c)
|
||||
if err != nil {
|
||||
return nil, msgKey, err
|
||||
}
|
||||
records, err := loadCommunityPasskeyCredentialRecords()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(records) == 0 {
|
||||
return nil, "ErrPasskeyNotConfigured", buserr.New("ErrPasskeyNotConfigured")
|
||||
}
|
||||
user, err := communityPasskeyUser(records, true)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
wa, err := webauthn.New(config)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
assertion, sessionData, err := wa.BeginLogin(user)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
sessionID := passkey.GetPasskeySessionStore().Set(passkey.PasskeySessionKindLogin, "", *sessionData)
|
||||
return &dto.PasskeyBeginResponse{SessionID: sessionID, PublicKey: assertion.Response}, "", nil
|
||||
}
|
||||
|
||||
func PasskeyFinishLogin(c *gin.Context, sessionID, entrance string) (*dto.UserLoginInfo, string, error) {
|
||||
if sessionID == "" {
|
||||
return nil, "ErrPasskeySession", buserr.New("ErrPasskeySession")
|
||||
}
|
||||
if err := CheckEntrance(entrance); err != nil {
|
||||
return nil, "ErrEntrance", err
|
||||
}
|
||||
config, msgKey, err := PasskeyConfig(c)
|
||||
if err != nil {
|
||||
return nil, msgKey, err
|
||||
}
|
||||
sessionStore := passkey.GetPasskeySessionStore()
|
||||
session, ok := sessionStore.Get(sessionID)
|
||||
if !ok || session.Kind != passkey.PasskeySessionKindLogin {
|
||||
return nil, "ErrPasskeySession", buserr.New("ErrPasskeySession")
|
||||
}
|
||||
sessionStore.Delete(sessionID)
|
||||
records, err := loadCommunityPasskeyCredentialRecords()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(records) == 0 {
|
||||
return nil, "ErrPasskeyNotConfigured", buserr.New("ErrPasskeyNotConfigured")
|
||||
}
|
||||
user, err := communityPasskeyUser(records, true)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
wa, err := webauthn.New(config)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
credential, err := wa.FinishLogin(user, session.Session, c.Request)
|
||||
if err != nil {
|
||||
return nil, "ErrAuth", err
|
||||
}
|
||||
if err := UpdatePasskeyCredentialRecord(records, credential); err != nil {
|
||||
return nil, "ErrAuth", err
|
||||
}
|
||||
if err := saveCommunityPasskeyCredentialRecords(records); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
userSetting, err := repo.NewISettingRepo().Get(repo.WithByKey("UserName"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
res, err := GenerateSession(c, psession.SessionUser{ID: psession.SuperAdminSessionUserID, Name: userSetting.Value, Role: "ADMIN"})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if entrance != "" {
|
||||
SetSecurityEntranceCookie(c, entrance)
|
||||
}
|
||||
return res, "", nil
|
||||
}
|
||||
|
||||
func PasskeyBeginRegister(c *gin.Context, name string) (*dto.PasskeyBeginResponse, string, error) {
|
||||
config, msgKey, err := PasskeyConfig(c)
|
||||
if err != nil {
|
||||
return nil, msgKey, err
|
||||
}
|
||||
records, err := loadCommunityPasskeyCredentialRecords()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(records) >= passkey.PasskeyMaxCredentials {
|
||||
return nil, "ErrPasskeyLimit", buserr.New("ErrPasskeyLimit")
|
||||
}
|
||||
user, err := communityPasskeyUser(records, true)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
wa, err := webauthn.New(config)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
exclusions := make([]protocol.CredentialDescriptor, len(user.Credentials))
|
||||
for i, credential := range user.Credentials {
|
||||
exclusions[i] = credential.Descriptor()
|
||||
}
|
||||
creation, sessionData, err := wa.BeginRegistration(user, webauthn.WithExclusions(exclusions))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
sessionID := passkey.GetPasskeySessionStore().Set(passkey.PasskeySessionKindRegister, strings.TrimSpace(name), *sessionData)
|
||||
return &dto.PasskeyBeginResponse{SessionID: sessionID, PublicKey: creation.Response}, "", nil
|
||||
}
|
||||
|
||||
func PasskeyFinishRegister(c *gin.Context, sessionID string) (string, error) {
|
||||
if sessionID == "" {
|
||||
return "ErrPasskeySession", buserr.New("ErrPasskeySession")
|
||||
}
|
||||
config, msgKey, err := PasskeyConfig(c)
|
||||
if err != nil {
|
||||
return msgKey, err
|
||||
}
|
||||
sessionStore := passkey.GetPasskeySessionStore()
|
||||
session, ok := sessionStore.Get(sessionID)
|
||||
if !ok || session.Kind != passkey.PasskeySessionKindRegister {
|
||||
return "ErrPasskeySession", buserr.New("ErrPasskeySession")
|
||||
}
|
||||
sessionStore.Delete(sessionID)
|
||||
records, err := loadCommunityPasskeyCredentialRecords()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(records) >= passkey.PasskeyMaxCredentials {
|
||||
return "ErrPasskeyLimit", buserr.New("ErrPasskeyLimit")
|
||||
}
|
||||
user, err := communityPasskeyUser(records, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
wa, err := webauthn.New(config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
credential, err := wa.FinishRegistration(user, session.Session, c.Request)
|
||||
if err != nil {
|
||||
return "ErrPasskeyVerify", err
|
||||
}
|
||||
if PasskeyCredentialExists(records, credential.ID) {
|
||||
return "ErrPasskeyDuplicate", buserr.New("ErrPasskeyDuplicate")
|
||||
}
|
||||
displayName := strings.TrimSpace(session.Name)
|
||||
if displayName == "" {
|
||||
displayName = fmt.Sprintf("%s-%s", passkey.PasskeyCredentialNameDefault, time.Now().Format("20060102150405"))
|
||||
}
|
||||
records = append(records, passkey.PasskeyCredentialRecord{
|
||||
ID: base64.RawURLEncoding.EncodeToString(credential.ID),
|
||||
Name: displayName,
|
||||
CreatedAt: time.Now().Format(constant.DateTimeLayout),
|
||||
LastUsedAt: "",
|
||||
FlagsValue: CredentialFlagsValue(credential.Flags),
|
||||
Credential: *credential,
|
||||
})
|
||||
if err := saveCommunityPasskeyCredentialRecords(records); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func PasskeyList() ([]dto.PasskeyInfo, error) {
|
||||
records, err := loadCommunityPasskeyCredentialRecords()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := make([]dto.PasskeyInfo, 0, len(records))
|
||||
for _, record := range records {
|
||||
list = append(list, dto.PasskeyInfo{ID: record.ID, Name: record.Name, CreatedAt: record.CreatedAt, LastUsedAt: record.LastUsedAt})
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func PasskeyDelete(id string) error {
|
||||
records, err := loadCommunityPasskeyCredentialRecords()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
index := -1
|
||||
for i, record := range records {
|
||||
if record.ID == id {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index == -1 {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
records = append(records[:index], records[index+1:]...)
|
||||
return saveCommunityPasskeyCredentialRecords(records)
|
||||
}
|
||||
|
||||
func ClearPasskeys() error {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
if err := settingRepo.Update(passkey.PasskeyUserIDSettingKey, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
return settingRepo.Update(passkey.PasskeyCredentialSettingKey, "")
|
||||
}
|
||||
|
||||
func communityPasskeyConfigured() (bool, error) {
|
||||
bindDomain, err := repo.NewISettingRepo().Get(repo.WithByKey("BindDomain"))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if strings.TrimSpace(bindDomain.Value) == "" {
|
||||
return false, nil
|
||||
}
|
||||
records, err := loadCommunityPasskeyCredentialRecords()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(records) > 0, nil
|
||||
}
|
||||
|
||||
func communityPasskeyUser(records []passkey.PasskeyCredentialRecord, allowCreate bool) (*passkey.PasskeyUser, error) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
storedUserID, err := settingRepo.Get(repo.WithByKey(passkey.PasskeyUserIDSettingKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawUserID, encodedUserID, err := GeneratePasskeyUserID(storedUserID.Value, allowCreate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if storedUserID.Value == "" && encodedUserID != "" {
|
||||
if err := settingRepo.Update(passkey.PasskeyUserIDSettingKey, encodedUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
nameSetting, err := settingRepo.Get(repo.WithByKey("UserName"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewPasskeyUser(rawUserID, nameSetting.Value, records), nil
|
||||
}
|
||||
|
||||
func loadCommunityPasskeyCredentialRecords() ([]passkey.PasskeyCredentialRecord, error) {
|
||||
setting, err := repo.NewISettingRepo().Get(repo.WithByKey(passkey.PasskeyCredentialSettingKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return LoadPasskeyCredentialRecords(setting.Value)
|
||||
}
|
||||
|
||||
func saveCommunityPasskeyCredentialRecords(records []passkey.PasskeyCredentialRecord) error {
|
||||
encoded, err := SavePasskeyCredentialRecords(records)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.NewISettingRepo().Update(passkey.PasskeyCredentialSettingKey, encoded)
|
||||
}
|
||||
|
||||
func PasskeyEnabled(c *gin.Context) (bool, error) {
|
||||
return strings.EqualFold(PasskeyRequestScheme(c), "https"), nil
|
||||
}
|
||||
|
||||
func PasskeyConfig(c *gin.Context) (*webauthn.Config, string, error) {
|
||||
enabled, err := PasskeyEnabled(c)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if !enabled {
|
||||
return nil, "ErrPasskeyDisabled", buserr.New("ErrPasskeyDisabled")
|
||||
}
|
||||
origin, rpID, err := PasskeyOriginAndRPID(c)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
panelName, err := repo.NewISettingRepo().Get(repo.WithByKey("PanelName"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return &webauthn.Config{
|
||||
RPID: rpID,
|
||||
RPDisplayName: panelName.Value,
|
||||
RPOrigins: []string{origin},
|
||||
AuthenticatorSelection: protocol.AuthenticatorSelection{
|
||||
UserVerification: protocol.VerificationRequired,
|
||||
},
|
||||
}, "", nil
|
||||
}
|
||||
|
||||
func PasskeyOriginAndRPID(c *gin.Context) (string, string, error) {
|
||||
host := passkeyRequestHost(c)
|
||||
if host == "" {
|
||||
return "", "", fmt.Errorf("missing request host")
|
||||
}
|
||||
scheme := PasskeyRequestScheme(c)
|
||||
origin := fmt.Sprintf("%s://%s", scheme, host)
|
||||
|
||||
bindDomain, err := repo.NewISettingRepo().Get(repo.WithByKey("BindDomain"))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
bindDomainValue := strings.TrimSpace(bindDomain.Value)
|
||||
if bindDomainValue == "" {
|
||||
return "", "", buserr.New("ErrPasskeyNotConfigured")
|
||||
}
|
||||
hostDomain := stripHostPort(host)
|
||||
bindDomainValue = stripHostPort(bindDomainValue)
|
||||
if hostDomain == "" || !strings.EqualFold(hostDomain, bindDomainValue) {
|
||||
return "", "", buserr.New("ErrPasskeyDisabled")
|
||||
}
|
||||
return origin, bindDomainValue, nil
|
||||
}
|
||||
|
||||
func NewPasskeyUser(userID []byte, name string, records []passkey.PasskeyCredentialRecord) *passkey.PasskeyUser {
|
||||
credentials := make([]webauthn.Credential, len(records))
|
||||
for i, record := range records {
|
||||
credentials[i] = record.Credential
|
||||
}
|
||||
return &passkey.PasskeyUser{
|
||||
ID: userID,
|
||||
Name: name,
|
||||
DisplayName: name,
|
||||
Credentials: credentials,
|
||||
}
|
||||
}
|
||||
|
||||
func GeneratePasskeyUserID(encoded string, allowCreate bool) ([]byte, string, error) {
|
||||
if encoded == "" {
|
||||
if !allowCreate {
|
||||
return nil, "", buserr.New("ErrPasskeyNotConfigured")
|
||||
}
|
||||
raw := make([]byte, 32)
|
||||
if _, err := rand.Read(raw); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return raw, base64.RawURLEncoding.EncodeToString(raw), nil
|
||||
}
|
||||
raw, err := base64.RawURLEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return raw, encoded, nil
|
||||
}
|
||||
|
||||
func LoadPasskeyCredentialRecords(encryptedValue string) ([]passkey.PasskeyCredentialRecord, error) {
|
||||
if encryptedValue == "" {
|
||||
return []passkey.PasskeyCredentialRecord{}, nil
|
||||
}
|
||||
decrypted, err := encrypt.StringDecrypt(encryptedValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var records []passkey.PasskeyCredentialRecord
|
||||
if err := json.Unmarshal([]byte(decrypted), &records); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range records {
|
||||
records[i].Credential.Flags = webauthn.NewCredentialFlags(protocol.AuthenticatorFlags(records[i].FlagsValue))
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func SavePasskeyCredentialRecords(records []passkey.PasskeyCredentialRecord) (string, error) {
|
||||
if len(records) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
copyRecords := make([]passkey.PasskeyCredentialRecord, len(records))
|
||||
copy(copyRecords, records)
|
||||
for i := range copyRecords {
|
||||
copyRecords[i].FlagsValue = CredentialFlagsValue(copyRecords[i].Credential.Flags)
|
||||
}
|
||||
raw, err := json.Marshal(copyRecords)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return encrypt.StringEncrypt(string(raw))
|
||||
}
|
||||
|
||||
func PasskeyCredentialExists(records []passkey.PasskeyCredentialRecord, credentialID []byte) bool {
|
||||
encoded := base64.RawURLEncoding.EncodeToString(credentialID)
|
||||
for _, record := range records {
|
||||
if record.ID == encoded {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func UpdatePasskeyCredentialRecord(records []passkey.PasskeyCredentialRecord, credential *webauthn.Credential) error {
|
||||
encoded := base64.RawURLEncoding.EncodeToString(credential.ID)
|
||||
for i := range records {
|
||||
if records[i].ID == encoded {
|
||||
records[i].Credential = *credential
|
||||
records[i].FlagsValue = CredentialFlagsValue(credential.Flags)
|
||||
records[i].LastUsedAt = time.Now().Format(constant.DateTimeLayout)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return buserr.New("ErrPasskeyNotConfigured")
|
||||
}
|
||||
|
||||
func CredentialFlagsValue(flags webauthn.CredentialFlags) uint8 {
|
||||
var value protocol.AuthenticatorFlags
|
||||
if flags.UserPresent {
|
||||
value |= protocol.FlagUserPresent
|
||||
}
|
||||
if flags.UserVerified {
|
||||
value |= protocol.FlagUserVerified
|
||||
}
|
||||
if flags.BackupEligible {
|
||||
value |= protocol.FlagBackupEligible
|
||||
}
|
||||
if flags.BackupState {
|
||||
value |= protocol.FlagBackupState
|
||||
}
|
||||
return uint8(value)
|
||||
}
|
||||
|
||||
func PasskeyRequestScheme(c *gin.Context) string {
|
||||
if c.Request.TLS != nil {
|
||||
return "https"
|
||||
}
|
||||
if !passkeyIsFromTrustedProxy(c) {
|
||||
return "http"
|
||||
}
|
||||
if proto := passkeyForwardedProto(c.GetHeader("Forwarded")); proto != "" {
|
||||
return proto
|
||||
}
|
||||
if proto := passkeyXForwardedProto(c.GetHeader("X-Forwarded-Proto")); proto != "" {
|
||||
return proto
|
||||
}
|
||||
return "http"
|
||||
}
|
||||
|
||||
func passkeyRequestHost(c *gin.Context) string {
|
||||
host := c.Request.Host
|
||||
if strings.Contains(host, ",") {
|
||||
host = strings.TrimSpace(strings.Split(host, ",")[0])
|
||||
}
|
||||
return strings.TrimSpace(host)
|
||||
}
|
||||
|
||||
func passkeyIsFromTrustedProxy(c *gin.Context) bool {
|
||||
remoteIP := passkeyRemoteIP(c.Request.RemoteAddr)
|
||||
if remoteIP == nil {
|
||||
return false
|
||||
}
|
||||
proxies, err := loadPasskeyTrustedProxies()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load passkey trusted proxies failed, err: %v", err)
|
||||
return false
|
||||
}
|
||||
for _, cidr := range proxies {
|
||||
if cidr.Contains(remoteIP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func passkeyRemoteIP(remoteAddr string) net.IP {
|
||||
if host, _, err := net.SplitHostPort(strings.TrimSpace(remoteAddr)); err == nil {
|
||||
return net.ParseIP(host)
|
||||
}
|
||||
return net.ParseIP(strings.TrimSpace(remoteAddr))
|
||||
}
|
||||
|
||||
func loadPasskeyTrustedProxies() ([]*net.IPNet, error) {
|
||||
setting, err := repo.NewISettingRepo().Get(repo.WithByKey("PasskeyTrustedProxies"))
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return parsePasskeyTrustedProxies("127.0.0.1\n::1")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return parsePasskeyTrustedProxies(setting.Value)
|
||||
}
|
||||
|
||||
func parsePasskeyTrustedProxies(value string) ([]*net.IPNet, error) {
|
||||
lines := strings.Split(value, "\n")
|
||||
proxies := make([]*net.IPNet, 0, len(lines))
|
||||
for _, line := range lines {
|
||||
entry := strings.TrimSpace(line)
|
||||
if entry == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(entry, "/") {
|
||||
if strings.Contains(entry, ":") {
|
||||
entry += "/128"
|
||||
} else {
|
||||
entry += "/32"
|
||||
}
|
||||
}
|
||||
_, cidr, err := net.ParseCIDR(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proxies = append(proxies, cidr)
|
||||
}
|
||||
return proxies, nil
|
||||
}
|
||||
|
||||
func passkeyForwardedProto(forwarded string) string {
|
||||
for _, part := range strings.Split(forwarded, ";") {
|
||||
item := strings.TrimSpace(part)
|
||||
if len(item) < 6 || !strings.EqualFold(item[:6], "proto=") {
|
||||
continue
|
||||
}
|
||||
proto := strings.Trim(strings.TrimSpace(item[6:]), `"`)
|
||||
if strings.EqualFold(proto, "https") {
|
||||
return "https"
|
||||
}
|
||||
if strings.EqualFold(proto, "http") {
|
||||
return "http"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func passkeyXForwardedProto(forwarded string) string {
|
||||
if forwarded == "" {
|
||||
return ""
|
||||
}
|
||||
proto := strings.TrimSpace(strings.Split(forwarded, ",")[0])
|
||||
if strings.EqualFold(proto, "https") {
|
||||
return "https"
|
||||
}
|
||||
if strings.EqualFold(proto, "http") {
|
||||
return "http"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func stripHostPort(host string) string {
|
||||
host = strings.TrimSpace(host)
|
||||
if host == "" {
|
||||
return ""
|
||||
}
|
||||
if strings.HasPrefix(host, "[") {
|
||||
if parsedHost, _, err := net.SplitHostPort(host); err == nil {
|
||||
return strings.Trim(parsedHost, "[]")
|
||||
}
|
||||
}
|
||||
if parsedHost, _, err := net.SplitHostPort(host); err == nil {
|
||||
return parsedHost
|
||||
}
|
||||
return strings.Trim(host, "[]")
|
||||
}
|
||||
@@ -47,3 +47,7 @@ type SystemSetting struct {
|
||||
Language string `json:"language"`
|
||||
IsIntl bool `json:"isIntl"`
|
||||
}
|
||||
|
||||
type PasskeyID struct {
|
||||
ID string `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -9,16 +9,15 @@ type SettingInfo struct {
|
||||
DeveloperMode string `json:"developerMode"`
|
||||
UpgradeBackupCopies string `json:"upgradeBackupCopies"`
|
||||
|
||||
SessionTimeout string `json:"sessionTimeout"`
|
||||
Port string `json:"port"`
|
||||
Ipv6 string `json:"ipv6"`
|
||||
BindAddress string `json:"bindAddress"`
|
||||
PanelName string `json:"panelName"`
|
||||
Edition string `json:"edition"`
|
||||
Theme string `json:"theme"`
|
||||
MenuTabs string `json:"menuTabs"`
|
||||
Language string `json:"language"`
|
||||
DocSource string `json:"docSource"`
|
||||
Port string `json:"port"`
|
||||
Ipv6 string `json:"ipv6"`
|
||||
BindAddress string `json:"bindAddress"`
|
||||
PanelName string `json:"panelName"`
|
||||
Edition string `json:"edition"`
|
||||
Theme string `json:"theme"`
|
||||
MenuTabs string `json:"menuTabs"`
|
||||
Language string `json:"language"`
|
||||
DocSource string `json:"docSource"`
|
||||
|
||||
ServerPort string `json:"serverPort"`
|
||||
SSL string `json:"ssl"`
|
||||
@@ -32,8 +31,6 @@ type SettingInfo struct {
|
||||
ExpirationDays string `json:"expirationDays"`
|
||||
ExpirationTime string `json:"expirationTime"`
|
||||
ComplexityVerification string `json:"complexityVerification"`
|
||||
MFAStatus string `json:"mfaStatus"`
|
||||
MFAInterval string `json:"mfaInterval"`
|
||||
|
||||
AppStoreVersion string `json:"appStoreVersion"`
|
||||
AppStoreLastModified string `json:"appStoreLastModified"`
|
||||
@@ -48,12 +45,56 @@ type SettingInfo struct {
|
||||
ProxyUser string `json:"proxyUser"`
|
||||
ProxyPasswd string `json:"proxyPasswd"`
|
||||
ProxyPasswdKeep string `json:"proxyPasswdKeep"`
|
||||
}
|
||||
|
||||
type SettingBaseInfo struct {
|
||||
SystemVersion string `json:"systemVersion"`
|
||||
DeveloperMode string `json:"developerMode"`
|
||||
UpgradeBackupCopies string `json:"upgradeBackupCopies"`
|
||||
|
||||
Port string `json:"port"`
|
||||
Ipv6 string `json:"ipv6"`
|
||||
BindAddress string `json:"bindAddress"`
|
||||
PanelName string `json:"panelName"`
|
||||
Edition string `json:"edition"`
|
||||
Theme string `json:"theme"`
|
||||
MenuTabs string `json:"menuTabs"`
|
||||
Language string `json:"language"`
|
||||
HideMenu string `json:"hideMenu"`
|
||||
DocSource string `json:"docSource"`
|
||||
|
||||
ServerPort string `json:"serverPort"`
|
||||
SecurityEntrance string `json:"securityEntrance"`
|
||||
ComplexityVerification string `json:"complexityVerification"`
|
||||
NoAuthSetting string `json:"noAuthSetting"`
|
||||
ProxyType string `json:"proxyType"`
|
||||
|
||||
DashboardMemoVisible string `json:"dashboardMemoVisible"`
|
||||
DashboardSimpleNodeVisible string `json:"dashboardSimpleNodeVisible"`
|
||||
}
|
||||
|
||||
type CurrentUserInfo struct {
|
||||
Name string `json:"name"`
|
||||
SessionTimeout int `json:"sessionTimeout"`
|
||||
MFAStatus string `json:"mfaStatus"`
|
||||
MFAInterval string `json:"mfaInterval"`
|
||||
ExpirationDays int `json:"expirationDays"`
|
||||
ExpirationTime string `json:"expirationTime"`
|
||||
ComplexitySetting string `json:"complexitySetting"`
|
||||
|
||||
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
IpWhiteList string `json:"ipWhiteList"`
|
||||
ApiKeyValidityTime string `json:"apiKeyValidityTime"`
|
||||
}
|
||||
type CurrentUserUpdate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Password string `json:"password"`
|
||||
OldPassword string `json:"oldPassword"`
|
||||
SessionTimeout int `json:"sessionTimeout" validate:"required,min=300,max=864000"`
|
||||
ExpirationDays int `json:"expirationDays" validate:"min=0,max=60"`
|
||||
ExpirationTime string `json:"expirationTime"`
|
||||
}
|
||||
|
||||
type SettingKey struct {
|
||||
Key string `json:"key" validate:"required,oneof=ScriptSync"`
|
||||
|
||||
@@ -8,22 +8,18 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/auth"
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
initauth "github.com/1Panel-dev/1Panel/core/init/auth"
|
||||
"github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/mfa"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/passkey"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
@@ -35,9 +31,7 @@ type AuthService struct{}
|
||||
type IAuthService interface {
|
||||
GetResponsePage() (string, error)
|
||||
VerifyCode(code string) (bool, error)
|
||||
Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error)
|
||||
LogOut(c *gin.Context) error
|
||||
MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error)
|
||||
PasskeyBeginLogin(c *gin.Context, entrance string) (*dto.PasskeyBeginResponse, string, error)
|
||||
PasskeyFinishLogin(c *gin.Context, sessionID, entrance string) (*dto.UserLoginInfo, string, error)
|
||||
PasskeyBeginRegister(c *gin.Context, name string) (*dto.PasskeyBeginResponse, string, error)
|
||||
@@ -53,104 +47,6 @@ func NewIAuthService() IAuthService {
|
||||
return &AuthService{}
|
||||
}
|
||||
|
||||
func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error) {
|
||||
nameSetting, err := settingRepo.Get(repo.WithByKey("UserName"))
|
||||
if err != nil {
|
||||
return nil, "", buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if nameSetting.Value != info.Name {
|
||||
return xpack.Login(c, info, entrance)
|
||||
}
|
||||
if err = checkPassword(info.Password); err != nil {
|
||||
return nil, "ErrAuth", err
|
||||
}
|
||||
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
|
||||
return nil, "ErrEntrance", buserr.New("ErrEntrance")
|
||||
}
|
||||
mfa, err := settingRepo.Get(repo.WithByKey("MFAStatus"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err = settingRepo.Update("Language", info.Language); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if mfa.Value == constant.StatusEnable {
|
||||
ip := common.GetRealClientIP(c)
|
||||
mfaSession := initauth.GetMFASessionStore().Set(nameSetting.Value, entrance, ip)
|
||||
return &dto.UserLoginInfo{Name: nameSetting.Value, MfaStatus: mfa.Value, MfaSession: mfaSession}, "", nil
|
||||
}
|
||||
res, err := u.generateSession(c, info.Name)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if entrance != "" {
|
||||
SetSecurityEntranceCookie(c, entrance)
|
||||
}
|
||||
return res, "", nil
|
||||
}
|
||||
|
||||
func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error) {
|
||||
mfaSessions := initauth.GetMFASessionStore()
|
||||
session, ok := mfaSessions.Get(info.SessionID)
|
||||
if !ok {
|
||||
return nil, "ErrMFA", nil
|
||||
}
|
||||
if session.IP != common.GetRealClientIP(c) {
|
||||
return nil, "ErrMFA", nil
|
||||
}
|
||||
if session.Entrance != entrance {
|
||||
return nil, "", buserr.New("ErrEntrance")
|
||||
}
|
||||
mfaSecret, err := settingRepo.Get(repo.WithByKey("MFASecret"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
mfaInterval, err := settingRepo.Get(repo.WithByKey("MFAInterval"))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
success := mfa.ValidCode(info.Code, mfaInterval.Value, mfaSecret.Value)
|
||||
if !success {
|
||||
return nil, "ErrMFA", nil
|
||||
}
|
||||
res, err := u.generateSession(c, session.Name)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
mfaSessions.Delete(info.SessionID)
|
||||
if entrance != "" {
|
||||
SetSecurityEntranceCookie(c, entrance)
|
||||
}
|
||||
return res, "", nil
|
||||
}
|
||||
|
||||
func (u *AuthService) generateSession(c *gin.Context, name string) (*dto.UserLoginInfo, error) {
|
||||
setting, err := settingRepo.Get(repo.WithByKey("SessionTimeout"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpsSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lifeTime, err := strconv.Atoi(setting.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessionUser := psession.SessionUser{ID: psession.SuperAdminSessionUserID, Name: name, Role: "ADMIN"}
|
||||
lifeTime = xpack.LoadSessionTimeout(sessionUser, lifeTime)
|
||||
if err := global.SESSION.SetFresh(c, sessionUser, httpsSetting.Value == constant.StatusEnable, lifeTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.UserLoginInfo{Name: name, Role: "ADMIN"}, nil
|
||||
}
|
||||
|
||||
func (u *AuthService) LogOut(c *gin.Context) error {
|
||||
httpsSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
|
||||
if err != nil {
|
||||
@@ -304,7 +200,8 @@ func (u *AuthService) PasskeyFinishLogin(c *gin.Context, sessionID, entrance str
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
res, err := u.generateSession(c, userSetting.Value)
|
||||
sessionUser := psession.SessionUser{ID: psession.SuperAdminSessionUserID, Name: userSetting.Value, Role: "ADMIN"}
|
||||
res, err := auth.GenerateSession(c, sessionUser)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
@@ -794,27 +691,3 @@ func stripHostPort(hostport string) string {
|
||||
}
|
||||
return strings.Trim(hostport, "[]")
|
||||
}
|
||||
func checkPassword(password string) error {
|
||||
priKey, _ := settingRepo.Get(repo.WithByKey("PASSWORD_PRIVATE_KEY"))
|
||||
|
||||
privateKey, err := encrypt.ParseRSAPrivateKey(priKey.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loginPassword, err := encrypt.DecryptPassword(password, privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
passwordSetting, err := settingRepo.Get(repo.WithByKey("Password"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
existPassword, err := encrypt.StringDecrypt(passwordSetting.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !hmac.Equal([]byte(loginPassword), []byte(existPassword)) {
|
||||
return buserr.New("ErrAuth")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func (u *BackupService) Create(req dto.BackupOperate) error {
|
||||
if err := backupRepo.Create(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xpack.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
global.LOG.Errorf("sync backup account to node failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
@@ -119,7 +119,7 @@ func (u *BackupService) Delete(name string) error {
|
||||
global.LOG.Errorf("check used of local cronjob failed, err: %v", err)
|
||||
return buserr.New("ErrBackupInUsed")
|
||||
}
|
||||
if err := xpack.CheckBackupUsed(name); err != nil {
|
||||
if err := xpack.MultiNodeProvider.CheckBackupUsed(name); err != nil {
|
||||
global.LOG.Errorf("check used of node cronjob failed, err: %v", err)
|
||||
return buserr.New("ErrBackupInUsed")
|
||||
}
|
||||
@@ -127,7 +127,7 @@ func (u *BackupService) Delete(name string) error {
|
||||
if err := backupRepo.Delete(repo.WithByName(name)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xpack.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
global.LOG.Errorf("sync backup account to node failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
@@ -176,7 +176,7 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
|
||||
if err := backupRepo.Save(&newBackup); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xpack.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
global.LOG.Errorf("sync backup account to node failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
@@ -218,7 +218,7 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) error {
|
||||
if err := backupRepo.Save(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xpack.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
global.LOG.Errorf("sync backup account to node failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -105,13 +105,13 @@ func (u *GroupService) Delete(id uint) error {
|
||||
case "command":
|
||||
err = commandRepo.UpdateGroup(id, defaultGroup.ID)
|
||||
case "node":
|
||||
err = xpack.UpdateGroup("node", id, defaultGroup.ID)
|
||||
err = xpack.MultiNodeProvider.UpdateGroup("node", id, defaultGroup.ID)
|
||||
case "website":
|
||||
bodyItem := []byte(fmt.Sprintf(`{"Group":%v, "NewGroup":%v}`, id, defaultGroup.ID))
|
||||
if _, err := proxy_local.NewLocalClient("/api/v2/websites/group/change", http.MethodPost, bytes.NewReader(bodyItem), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xpack.UpdateGroup("node", id, defaultGroup.ID); err != nil {
|
||||
if err := xpack.MultiNodeProvider.UpdateGroup("node", id, defaultGroup.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func (u *ScriptService) Create(req dto.ScriptOperate) error {
|
||||
if req.IsInteractive {
|
||||
return nil
|
||||
}
|
||||
if err := xpack.Sync(constant.SyncScripts); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncScripts); err != nil {
|
||||
global.LOG.Errorf("sync scripts to node failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
@@ -140,7 +140,7 @@ func (u *ScriptService) Delete(req dto.OperateByIDs) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := xpack.Sync(constant.SyncScripts); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncScripts); err != nil {
|
||||
global.LOG.Errorf("sync scripts to node failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
@@ -160,7 +160,7 @@ func (u *ScriptService) Update(req dto.ScriptOperate) error {
|
||||
if err := scriptRepo.Update(req.ID, updateMap); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xpack.Sync(constant.SyncScripts); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncScripts); err != nil {
|
||||
global.LOG.Errorf("sync scripts to node failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
@@ -276,7 +276,7 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
|
||||
if err := global.DB.Model(&model.Setting{}).Where("key = ?", "ScriptVersion").Updates(map[string]interface{}{"value": string(versionRes)}).Error; err != nil {
|
||||
return fmt.Errorf("update script version in db failed, err: %v", err)
|
||||
}
|
||||
if err := xpack.Sync(constant.SyncScripts); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncScripts); err != nil {
|
||||
global.LOG.Errorf("sync scripts to node failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -43,16 +43,13 @@ type SettingService struct{}
|
||||
|
||||
type ISettingService interface {
|
||||
GetSettingInfo() (*dto.SettingInfo, error)
|
||||
GetSettingBaseInfo() (*dto.SettingBaseInfo, error)
|
||||
LoadInterfaceAddr() ([]string, error)
|
||||
Update(c *gin.Context, key, value string) error
|
||||
UpdatePassword(c *gin.Context, old, new string) error
|
||||
UpdatePort(port uint) error
|
||||
UpdateBindInfo(req dto.BindInfo) error
|
||||
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
|
||||
|
||||
UpdateProxy(req dto.ProxyUpdate) error
|
||||
|
||||
@@ -113,6 +110,40 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
|
||||
return &info, err
|
||||
}
|
||||
|
||||
func (u *SettingService) GetSettingBaseInfo() (*dto.SettingBaseInfo, error) {
|
||||
setting, err := settingRepo.List()
|
||||
if err != nil {
|
||||
return nil, buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
settingMap := make(map[string]string)
|
||||
for _, set := range setting {
|
||||
settingMap[set.Key] = set.Value
|
||||
}
|
||||
if hideMenu, ok := settingMap["HideMenu"]; ok && len(hideMenu) > 0 {
|
||||
var menus []dto.ShowMenu
|
||||
if err := json.Unmarshal([]byte(hideMenu), &menus); err == nil {
|
||||
sortShowMenus(menus)
|
||||
if sortedBytes, err := json.Marshal(menus); err == nil {
|
||||
settingMap["HideMenu"] = string(sortedBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
var info dto.SettingBaseInfo
|
||||
arr, err := json.Marshal(settingMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(arr, &info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Edition == "" {
|
||||
info.Edition = "cn"
|
||||
_ = settingRepo.UpdateOrCreate("Edition", info.Edition)
|
||||
}
|
||||
|
||||
return &info, err
|
||||
}
|
||||
|
||||
func sortShowMenus(menus []dto.ShowMenu) {
|
||||
for i := range menus {
|
||||
if len(menus[i].Children) > 0 {
|
||||
@@ -164,14 +195,6 @@ func (u *SettingService) Update(c *gin.Context, key, value string) error {
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "ExpirationDays":
|
||||
timeout, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format(constant.DateTimeLayout)); err != nil {
|
||||
return err
|
||||
}
|
||||
case "BindDomain":
|
||||
if len(value) != 0 {
|
||||
_ = global.SESSION.Clean()
|
||||
@@ -179,11 +202,9 @@ func (u *SettingService) Update(c *gin.Context, key, value string) error {
|
||||
if err := u.clearPasskeySettings(); err != nil {
|
||||
return err
|
||||
}
|
||||
case "UserName", "Password":
|
||||
u.deleteCurrentSession(c)
|
||||
case "Language":
|
||||
i18n.SetCachedDBLanguage(value)
|
||||
if err := xpack.Sync(constant.SyncLanguage); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncLanguage); err != nil {
|
||||
global.LOG.Errorf("sync language to node failed, err: %v", err)
|
||||
}
|
||||
case "UpgradeBackupCopies":
|
||||
@@ -196,7 +217,7 @@ func (u *SettingService) Update(c *gin.Context, key, value string) error {
|
||||
}
|
||||
case "Edition":
|
||||
global.CONF.Base.Edition = value
|
||||
if err := xpack.Sync(constant.SyncEdition); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncEdition); err != nil {
|
||||
global.LOG.Errorf("sync edition to node failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -263,14 +284,14 @@ func (u *SettingService) UpdateProxy(req dto.ProxyUpdate) error {
|
||||
if err := settingRepo.Update("ProxyPasswdKeep", req.ProxyPasswdKeep); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xpack.ProxyDocker(loadDockerProxy(req)); err != nil {
|
||||
if err := xpack.MultiNodeProvider.ProxyDocker(loadDockerProxy(req)); err != nil {
|
||||
return err
|
||||
}
|
||||
syncScope := constant.SyncSystemProxy
|
||||
if req.WithDockerRestart {
|
||||
syncScope = constant.SyncSystemProxyWithRestartDocker
|
||||
}
|
||||
if err := xpack.Sync(syncScope); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(syncScope); err != nil {
|
||||
global.LOG.Errorf("sync proxy to node failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
@@ -410,7 +431,7 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
||||
if err := os.Rename(path.Join(secretDir, "server.key.tmp"), path.Join(secretDir, "server.key")); err != nil {
|
||||
return err
|
||||
}
|
||||
status, _ := settingRepo.GetValueByKey("SSL")
|
||||
status := global.CONF.Conn.SSL
|
||||
if req.SSL != status {
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
@@ -420,6 +441,7 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
|
||||
if err := settingRepo.Update("SSL", req.SSL); err != nil {
|
||||
return err
|
||||
}
|
||||
global.CONF.Conn.SSL = req.SSL
|
||||
return u.UpdateSystemSSL()
|
||||
}
|
||||
|
||||
@@ -562,14 +584,6 @@ func (u *SettingService) UpdateTerminal(req dto.TerminalInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
|
||||
if err := u.HandlePasswordExpired(c, old, new); err != nil {
|
||||
return err
|
||||
}
|
||||
u.deleteCurrentSession(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) deleteCurrentSession(c *gin.Context) {
|
||||
if c == nil {
|
||||
return
|
||||
@@ -588,7 +602,7 @@ func (u *SettingService) clearPasskeySettings() error {
|
||||
if err := settingRepo.Update(passkey.PasskeyCredentialSettingKey, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return xpack.AuthProvider.ClearPasskeys()
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdateSystemSSL() error {
|
||||
@@ -610,35 +624,6 @@ func (u *SettingService) UpdateSystemSSL() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) GenerateApiKey() (string, error) {
|
||||
apiKey := common.RandStr(32)
|
||||
if err := settingRepo.Update("ApiKey", apiKey); err != nil {
|
||||
return global.Api.ApiKey, err
|
||||
}
|
||||
global.Api.ApiKey = apiKey
|
||||
return apiKey, nil
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdateApiConfig(req dto.ApiInterfaceConfig) error {
|
||||
if err := settingRepo.UpdateOrCreate("ApiInterfaceStatus", req.ApiInterfaceStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
global.Api.ApiInterfaceStatus = req.ApiInterfaceStatus
|
||||
if err := settingRepo.UpdateOrCreate("ApiKey", req.ApiKey); err != nil {
|
||||
return err
|
||||
}
|
||||
global.Api.ApiKey = req.ApiKey
|
||||
if err := settingRepo.UpdateOrCreate("IpWhiteList", req.IpWhiteList); err != nil {
|
||||
return err
|
||||
}
|
||||
global.Api.IpWhiteList = req.IpWhiteList
|
||||
if err := settingRepo.UpdateOrCreate("ApiKeyValidityTime", req.ApiKeyValidityTime); err != nil {
|
||||
return err
|
||||
}
|
||||
global.Api.ApiKeyValidityTime = req.ApiKeyValidityTime
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadInfoFromCert() (dto.SSLInfo, error) {
|
||||
var info dto.SSLInfo
|
||||
certFile := path.Join(global.CONF.Base.InstallDir, "1panel/secret/server.crt")
|
||||
|
||||
@@ -259,7 +259,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
|
||||
global.LOG.Info("upgrade successful!")
|
||||
dropBackupCopies()
|
||||
xpack.AutoUpgradeWithMaster()
|
||||
xpack.MultiNodeProvider.AutoUpgradeWithMaster()
|
||||
go writeLogs(req.Version)
|
||||
_ = settingRepo.Update("SystemVersion", req.Version)
|
||||
_ = global.AgentDB.Model(&model.Setting{}).Where("key = ?", "SystemVersion").Updates(map[string]interface{}{"value": req.Version}).Error
|
||||
|
||||
@@ -4,7 +4,7 @@ base:
|
||||
is_demo: false
|
||||
is_offline: false
|
||||
is_fxplay: false
|
||||
is_xpackee: true
|
||||
is_xpackee: false
|
||||
port: 9999
|
||||
username: admin
|
||||
password: admin123
|
||||
|
||||
Binary file not shown.
@@ -30,13 +30,6 @@ type Conn struct {
|
||||
Entrance string `mapstructure:"entrance"`
|
||||
}
|
||||
|
||||
type ApiInterface struct {
|
||||
ApiKey string `mapstructure:"api_key"`
|
||||
ApiInterfaceStatus string `mapstructure:"api_interface_status"`
|
||||
IpWhiteList string `mapstructure:"ip_white_list"`
|
||||
ApiKeyValidityTime string `mapstructure:"api_key_validity_time"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
Level string `mapstructure:"level"`
|
||||
TimeZone string `mapstructure:"timeZone"`
|
||||
|
||||
@@ -18,7 +18,6 @@ var (
|
||||
AgentDB *gorm.DB
|
||||
LOG *logrus.Logger
|
||||
CONF ServerConfig
|
||||
Api ApiInterface
|
||||
VALID *validator.Validate
|
||||
SESSION *psession.PSession
|
||||
Viper *viper.Viper
|
||||
|
||||
@@ -55,8 +55,6 @@ AppInstallCheck: 'Check application installation environment'
|
||||
|
||||
# backup
|
||||
ErrBackupInUsed: "This backup account is used in scheduled tasks and cannot be deleted"
|
||||
ErrRolePresetCannotDelete: "System preset roles cannot be deleted"
|
||||
ErrRoleBoundToUser: "This role is already bound to users and cannot be deleted"
|
||||
ErrBackupCheck: "Backup account connection test failed {{ .err }}"
|
||||
ErrBackupLocal: "Local server backup account does not support this operation!"
|
||||
ErrBackupPublic: "Detected that this backup account is not public, check and try again!"
|
||||
@@ -75,6 +73,7 @@ ErrLicenseExist: "This license record already exists. You can directly go to the
|
||||
ErrXpackNotFound: "This section requires Business Edition. Import a license in Panel Settings > License."
|
||||
ErrXpackExceptional: "This section requires Business Edition. Sync the license status in Panel Settings > License."
|
||||
ErrXpackLost: "The license retry limit has been reached. Go to Panel Settings > License and run a manual sync."
|
||||
ErrXpackEELicenseRequired: "Enterprise Edition license is not imported. Import a license first."
|
||||
ErrDeviceLost: "Required files for license verification are missing, check and try again!"
|
||||
ErrDeviceErr: "Current environment does not match the license import environment. Edit the license and re-import."
|
||||
ErrXpackTimeout: "Request timeout, network connection might be unstable, try again later!"
|
||||
@@ -92,6 +91,19 @@ ErrNodeBind: "This node is already bound to a license, check and retry!"
|
||||
ErrNodeLocalRollback: "The primary node does not support direct rollback. Run '1pctl restore' manually to roll back."
|
||||
ErrIntlLicense: "The current version does not support importing international licenses yet. Stay tuned!"
|
||||
|
||||
# user
|
||||
ErrNoSuchUser: "User information not found, check and try again!"
|
||||
ErrToMaster: "This user does not have permission to perform this operation on the master node. Check and try again!"
|
||||
ErrToNode: "This user does not have permission to perform this operation on this node. Check and try again!"
|
||||
ErrRBAC: "Insufficient user permissions. This operation cannot be performed. Check and try again!"
|
||||
ErrNoneNode: "This user has no node permissions. Contact an administrator or check and try again!"
|
||||
ErrOnlySuperAdmin: "Only super admins can use this feature!"
|
||||
ErrRolePresetCannotDelete: "System preset roles cannot be deleted"
|
||||
ErrRolePresetCannotUpdate: "System preset roles cannot be updated"
|
||||
ErrRoleBoundToUser: "This role is already bound to users and cannot be deleted"
|
||||
ErrRolePresetCannotBind: "System preset roles cannot be assigned to users"
|
||||
ErrSuperAdminCannotDelete: "Super admin users cannot be deleted"
|
||||
|
||||
InvalidRequestBodyType: "Invalid request body format. Ensure content matches the required format and retry."
|
||||
InvalidLicenseCodeType: "Invalid license code format provided, check and try again!"
|
||||
LicenseNotFoundType: "License not found, no matching record exists in the system for the provided license. Check and try again!"
|
||||
|
||||
@@ -54,8 +54,6 @@ AppInstallCheck: 'Verificar entorno de instalación de aplicación'
|
||||
|
||||
# backup
|
||||
ErrBackupInUsed: 'Cuenta de respaldo en uso por tarea programada'
|
||||
ErrRolePresetCannotDelete: "System preset roles cannot be deleted"
|
||||
ErrRoleBoundToUser: "This role is already bound to users and cannot be deleted"
|
||||
ErrBackupCheck: 'Conexión de respaldo falló: {{ .err }}'
|
||||
ErrBackupLocal: "La cuenta de respaldo del servidor local no admite esta operación"
|
||||
ErrBackupPublic: "Se detectó que esta cuenta de respaldo no es pública, verifique e intente de nuevo"
|
||||
@@ -75,6 +73,7 @@ ErrXpackNotFound: "Esta sección requiere la edición Business. Importa una lice
|
||||
ErrXpackExceptional: "Esta sección requiere la edición Business. Sincroniza el estado de la licencia en Configuración del panel > Licencia."
|
||||
ErrXpackOutOfDate: "La licencia actual ha expirado, importe nuevamente la licencia en Panel > Ajustes > Licencia"
|
||||
ErrXpackLost: "La licencia alcanzó el límite de reintentos. Ve a Configuración del panel > Licencia y ejecuta una sincronización manual."
|
||||
ErrXpackEELicenseRequired: "La licencia de la edición Enterprise no se ha importado. Importa primero una licencia."
|
||||
ErrDeviceLost: "Faltan archivos necesarios para la verificación de licencia, verifique e intente de nuevo"
|
||||
ErrDeviceErr: "El entorno actual no coincide con el entorno de importación de la licencia. Edite la licencia e impórtela de nuevo"
|
||||
ErrXpackTimeout: "Tiempo de espera de la solicitud, puede que la conexión de red sea inestable, intente más tarde"
|
||||
@@ -91,6 +90,20 @@ ErrNodeUnbind: "Este nodo no está dentro del rango de vinculación de la licenc
|
||||
ErrNodeBind: "Este nodo ya está vinculado a una licencia, verifique e intente de nuevo"
|
||||
ErrNodeLocalRollback: "El nodo principal no admite la reversión directa. Ejecuta manualmente '1pctl restore' para revertir."
|
||||
ErrIntlLicense: "La versión actual aún no admite importar licencias de la edición internacional. Próximamente"
|
||||
|
||||
# user
|
||||
ErrNoSuchUser: "No se encontró la información del usuario. Verifique e intente de nuevo."
|
||||
ErrToMaster: "Este usuario no tiene permiso para realizar esta operación en el nodo principal. Verifique e intente de nuevo."
|
||||
ErrToNode: "Este usuario no tiene permiso para realizar esta operación en este nodo. Verifique e intente de nuevo."
|
||||
ErrRBAC: "Permisos de usuario insuficientes. No se puede realizar esta operación. Verifique e intente de nuevo."
|
||||
ErrNoneNode: "Este usuario no tiene permisos sobre ningún nodo. Contacte al administrador o verifique e intente de nuevo."
|
||||
ErrOnlySuperAdmin: "Solo los superadministradores pueden usar esta función."
|
||||
ErrRolePresetCannotDelete: "No se pueden eliminar los roles predefinidos del sistema"
|
||||
ErrRolePresetCannotUpdate: "No se pueden actualizar los roles predefinidos del sistema"
|
||||
ErrRoleBoundToUser: "Este rol ya está vinculado a usuarios y no se puede eliminar"
|
||||
ErrRolePresetCannotBind: "Los roles predefinidos del sistema no se pueden asignar a usuarios"
|
||||
ErrSuperAdminCannotDelete: "No se pueden eliminar usuarios superadministradores"
|
||||
|
||||
InvalidRequestBodyType: "Formato del cuerpo de la solicitud no válido, por favor revisa y asegúrate de que el contenido cumpla con el formato requerido antes de reintentar."
|
||||
InvalidLicenseCodeType: "Formato de código de licencia no válido, por favor revisa e inténtalo de nuevo."
|
||||
LicenseNotFoundType: "Licencia no encontrada, no existe ningún registro coincidente en el sistema para la licencia proporcionada. Por favor revisa e inténtalo de nuevo."
|
||||
|
||||
@@ -49,8 +49,6 @@ AppInstallCheck: 'アプリケーションインストール環境を確認'
|
||||
|
||||
# backup
|
||||
ErrBackupInUsed: 'バックアップアカウントがスケジュールで使用中'
|
||||
ErrRolePresetCannotDelete: "System preset roles cannot be deleted"
|
||||
ErrRoleBoundToUser: "This role is already bound to users and cannot be deleted"
|
||||
ErrBackupCheck: '接続テストに失敗しました: {{ .err }}'
|
||||
ErrBackupLocal: "ローカルサーバーバックアップアカウントはこの操作をサポートしていません!"
|
||||
ErrBackupPublic: "このバックアップアカウントが公開されていないと検出されました。再確認してください!"
|
||||
@@ -70,6 +68,7 @@ ErrLicenseExist: "このライセンス記録は既に存在します。ライ
|
||||
ErrXpackNotFound: "この機能は Business Edition で利用できます。パネル設定 > ライセンス でライセンスをインポートしてください"
|
||||
ErrXpackExceptional: "この機能は Business Edition で利用できます。パネル設定 > ライセンス でライセンス状態を同期してください"
|
||||
ErrXpackLost: "ライセンスの再試行回数が上限に達しました。パネル設定 > ライセンス で手動同期を実行してください"
|
||||
ErrXpackEELicenseRequired: "Enterprise Edition のライセンスがインポートされていません。先にライセンスをインポートしてください"
|
||||
ErrDeviceLost: "ライセンス検証に必要なファイルが失われました。確認して再試行してください!"
|
||||
ErrDeviceErr: "現在の環境とライセンスのインポート環境が一致しません。ライセンスを編集して再度インポートしてください!"
|
||||
ErrXpackTimeout: "リクエストタイムアウト、ネットワーク接続が不安定な可能性があります。後で再試行してください"
|
||||
@@ -87,6 +86,19 @@ ErrNodeBind: "そのノードはライセンスにバインドされています
|
||||
ErrNodeLocalRollback: "マスターノードでは直接ロールバックできません。'1pctl restore' を手動で実行してロールバックしてください"
|
||||
ErrIntlLicense: "現在のバージョンでは国際版ライセンスのインポートはまだサポートされていません。しばらくお待ちください!"
|
||||
|
||||
# user
|
||||
ErrNoSuchUser: "ユーザー情報が見つかりません。確認して再試行してください!"
|
||||
ErrToMaster: "このユーザーにはマスターノードでこの操作を実行する権限がありません。確認して再試行してください!"
|
||||
ErrToNode: "このユーザーにはこのノードでこの操作を実行する権限がありません。確認して再試行してください!"
|
||||
ErrRBAC: "ユーザー権限が不足しているため、この操作を実行できません。確認して再試行してください!"
|
||||
ErrNoneNode: "このユーザーにはノード権限がありません。管理者に連絡するか、確認して再試行してください!"
|
||||
ErrOnlySuperAdmin: "この機能はスーパー管理者のみ使用できます!"
|
||||
ErrRolePresetCannotDelete: "システム既定ロールは削除できません"
|
||||
ErrRolePresetCannotUpdate: "システム既定ロールは更新できません"
|
||||
ErrRoleBoundToUser: "このロールはすでにユーザーに紐付いているため削除できません"
|
||||
ErrRolePresetCannotBind: "システム既定ロールはユーザーに割り当てできません"
|
||||
ErrSuperAdminCannotDelete: "スーパー管理者ユーザーは削除できません"
|
||||
|
||||
InvalidRequestBodyType: "リクエストボディの形式が無効です。内容が要求された形式に準拠しているか確認してから再試行してください!"
|
||||
InvalidLicenseCodeType: "提供されたライセンスコードの形式が無効です。確認してから再試行してください!"
|
||||
LicenseNotFoundType: "ライセンスが見つかりません。提供されたライセンスに一致する記録がシステム内に存在しません。確認してから再試行してください!"
|
||||
|
||||
@@ -49,8 +49,6 @@ AppInstallCheck: '애플리케이션 설치 환경 확인'
|
||||
|
||||
# backup
|
||||
ErrBackupInUsed: '백업 계정이 예약에 사용 중'
|
||||
ErrRolePresetCannotDelete: "System preset roles cannot be deleted"
|
||||
ErrRoleBoundToUser: "This role is already bound to users and cannot be deleted"
|
||||
ErrBackupCheck: '연결 테스트 실패: {{ .err }}'
|
||||
ErrBackupLocal: "로컬 서버 백업 계정은 이 작업을 지원하지 않습니다"
|
||||
ErrBackupPublic: "이 백업 계정이 공개된 것으로 감지되지 않았습니다. 다시 확인하십시오"
|
||||
@@ -69,6 +67,7 @@ ErrLicenseExist: "해당 라이선스 기록이 이미 존재합니다. 라이
|
||||
ErrXpackNotFound: "이 기능은 Business Edition에서 사용할 수 있습니다. 패널 설정 > 라이선스에서 라이선스를 가져오세요"
|
||||
ErrXpackExceptional: "이 기능은 Business Edition에서 사용할 수 있습니다. 패널 설정 > 라이선스에서 라이선스 상태를 동기화하세요"
|
||||
ErrXpackLost: "라이선스 재시도 한도에 도달했습니다. 패널 설정 > 라이선스에서 수동 동기화를 실행하세요"
|
||||
ErrXpackEELicenseRequired: "Enterprise Edition 라이선스를 가져오지 않았습니다. 먼저 라이선스를 가져오세요"
|
||||
ErrDeviceLost: "라이센스 검증에 필요한 파일이 누락되었습니다. 확인 후 다시 시도해 주세요"
|
||||
ErrDeviceErr: "현재 환경이 라이선스 가져오기 환경과 일치하지 않습니다. 라이선스를 편집하고 다시 가져오십시오"
|
||||
ErrXpackTimeout: "요청 시간 초과, 네트워크 연결이 불안정할 수 있습니다. 나중에 다시 시도해 주세요"
|
||||
@@ -86,6 +85,19 @@ ErrNodeBind: "이 노드가 이미 라이선스에 바인딩되어 있음을 감
|
||||
ErrNodeLocalRollback: "마스터 노드는 직접 롤백을 지원하지 않습니다. '1pctl restore'를 수동으로 실행해 롤백하세요"
|
||||
ErrIntlLicense: "현재 버전에서는 국제판 라이선스 가져오기를 아직 지원하지 않습니다. 곧 제공될 예정입니다"
|
||||
|
||||
# user
|
||||
ErrNoSuchUser: "사용자 정보를 찾을 수 없습니다. 확인 후 다시 시도하세요"
|
||||
ErrToMaster: "이 사용자는 마스터 노드에서 이 작업을 수행할 권한이 없습니다. 확인 후 다시 시도하세요"
|
||||
ErrToNode: "이 사용자는 이 노드에서 이 작업을 수행할 권한이 없습니다. 확인 후 다시 시도하세요"
|
||||
ErrRBAC: "사용자 권한이 부족하여 이 작업을 수행할 수 없습니다. 확인 후 다시 시도하세요"
|
||||
ErrNoneNode: "이 사용자에게는 노드 권한이 없습니다. 관리자에게 문의하거나 확인 후 다시 시도하세요"
|
||||
ErrOnlySuperAdmin: "슈퍼 관리자만 이 기능을 사용할 수 있습니다"
|
||||
ErrRolePresetCannotDelete: "시스템 기본 역할은 삭제할 수 없습니다"
|
||||
ErrRolePresetCannotUpdate: "시스템 기본 역할은 수정할 수 없습니다"
|
||||
ErrRoleBoundToUser: "이 역할은 이미 사용자에게 연결되어 있어 삭제할 수 없습니다"
|
||||
ErrRolePresetCannotBind: "시스템 기본 역할은 사용자에게 할당할 수 없습니다"
|
||||
ErrSuperAdminCannotDelete: "슈퍼 관리자 사용자는 삭제할 수 없습니다"
|
||||
|
||||
InvalidRequestBodyType: "요청 본문 형식이 잘못되었습니다. 내용이 형식 요구 사항을 충족하는지 확인한 후 다시 시도하세요"
|
||||
InvalidLicenseCodeType: "제공된 라이선스 코드 형식이 잘못되었습니다. 확인 후 다시 시도하세요"
|
||||
LicenseNotFoundType: "라이선스가 존재하지 않습니다. 시스템에서 제공된 라이선스와 일치하는 기록을 찾을 수 없습니다. 확인 후 다시 시도하세요"
|
||||
|
||||
@@ -44,8 +44,6 @@ ErrFileNotFound: "Fail {{ .name }} tidak wujud"
|
||||
|
||||
# backup
|
||||
ErrBackupInUsed: 'Akaun sandaran sedang digunakan oleh tugas'
|
||||
ErrRolePresetCannotDelete: "System preset roles cannot be deleted"
|
||||
ErrRoleBoundToUser: "This role is already bound to users and cannot be deleted"
|
||||
ErrBackupCheck: 'Ujian sambungan gagal: {{ .err }}'
|
||||
ErrBackupLocal: "Akaun sandaran pelayan tempatan tidak menyokong operasi ini"
|
||||
ErrBackupPublic: "Akaun sandaran ini dikesan tidak awam, sila semak semula dan cuba lagi"
|
||||
@@ -68,6 +66,7 @@ ErrDeviceLost: "Fail yang diperlukan untuk pengesahan lesen hilang, sila semak d
|
||||
ErrDeviceErr: "Persekitaran semasa tidak sepadan dengan persekitaran import lesen. Sila edit lesen dan import semula"
|
||||
ErrXpackTimeout: "Permintaan tamat masa, sambungan rangkaian mungkin tidak stabil, sila cuba lagi kemudian"
|
||||
ErrUnbindMaster: "Terdapat nod dalam pengurusan nod, sila keluarkan dahulu dan cuba lagi"
|
||||
ErrXpackEELicenseRequired: "Lesen Edisi Enterprise belum diimport. Sila import lesen terlebih dahulu"
|
||||
ErrFreeNodeLimit: "Had nod Edisi Community telah dicapai. Pergi ke www.lxware.cn/1panel untuk naik taraf dan cuba lagi"
|
||||
ErrNodeBound: "Lesen ini telah diikat dengan nod lain, sila semak dan cuba lagi"
|
||||
ErrNodeBoundDelete: "Lisensi ini telah diikat dan tidak menyokong operasi penghapusan. Sila semak dan cuba lagi"
|
||||
@@ -81,6 +80,19 @@ ErrNodeBind: "Nod ini telah diikat dengan lesen, sila semak dan cuba lagi"
|
||||
ErrNodeLocalRollback: "Nod utama tidak menyokong rollback secara langsung. Jalankan '1pctl restore' secara manual untuk rollback"
|
||||
ErrIntlLicense: "Versi semasa belum menyokong import lesen edisi antarabangsa. Nantikan"
|
||||
|
||||
# user
|
||||
ErrNoSuchUser: "Maklumat pengguna tidak ditemui, sila semak dan cuba lagi"
|
||||
ErrToMaster: "Pengguna ini tiada kebenaran untuk melakukan operasi ini pada nod utama, sila semak dan cuba lagi"
|
||||
ErrToNode: "Pengguna ini tiada kebenaran untuk melakukan operasi ini pada nod ini, sila semak dan cuba lagi"
|
||||
ErrRBAC: "Kebenaran pengguna tidak mencukupi, operasi ini tidak boleh dilakukan. Sila semak dan cuba lagi"
|
||||
ErrNoneNode: "Pengguna ini tidak mempunyai sebarang kebenaran nod. Sila hubungi pentadbir atau semak dan cuba lagi"
|
||||
ErrOnlySuperAdmin: "Hanya super admin boleh menggunakan fungsi ini"
|
||||
ErrRolePresetCannotDelete: "Peranan pratetap sistem tidak boleh dipadam"
|
||||
ErrRolePresetCannotUpdate: "Peranan pratetap sistem tidak boleh dikemas kini"
|
||||
ErrRoleBoundToUser: "Peranan ini sudah terikat kepada pengguna dan tidak boleh dipadam"
|
||||
ErrRolePresetCannotBind: "Peranan pratetap sistem tidak boleh diberikan kepada pengguna"
|
||||
ErrSuperAdminCannotDelete: "Pengguna super admin tidak boleh dipadam"
|
||||
|
||||
InvalidRequestBodyType: "Format badan permintaan tidak sah, sila periksa dan pastikan kandungan memenuhi format yang diperlukan sebelum mencuba semula"
|
||||
InvalidLicenseCodeType: "Format kod lesen yang diberikan tidak sah, sila periksa dan cuba lagi"
|
||||
LicenseNotFoundType: "Lesen tidak dijumpai, tiada rekod yang sepadan dengan lesen yang diberikan dalam sistem. Sila periksa dan cuba lagi"
|
||||
|
||||
@@ -49,8 +49,6 @@ AppInstallCheck: 'Verificar ambiente de instalação da aplicação'
|
||||
|
||||
# backup
|
||||
ErrBackupInUsed: 'Conta de backup em uso por tarefa'
|
||||
ErrRolePresetCannotDelete: "System preset roles cannot be deleted"
|
||||
ErrRoleBoundToUser: "This role is already bound to users and cannot be deleted"
|
||||
ErrBackupCheck: 'Teste de conexão falhou: {{ .err }}'
|
||||
ErrBackupLocal: "A conta de backup do servidor local não suporta essa operação"
|
||||
ErrBackupPublic: "A conta de backup detectada não é pública, por favor verifique e tente novamente"
|
||||
@@ -69,6 +67,7 @@ ErrLicenseExist: "Este registro de licença já existe. Você pode ir diretament
|
||||
ErrXpackNotFound: "Este recurso requer a edição Business. Importe uma licença em Configurações do Painel > Licença."
|
||||
ErrXpackExceptional: "Este recurso requer a edição Business. Sincronize o status da licença em Configurações do Painel > Licença."
|
||||
ErrXpackLost: "A licença atingiu o limite de tentativas. Acesse Configurações do Painel > Licença e execute uma sincronização manual."
|
||||
ErrXpackEELicenseRequired: "A licença da edição Enterprise não foi importada. Importe uma licença primeiro."
|
||||
ErrDeviceLost: "Arquivos necessários para a verificação da licença estão faltando, por favor verifique e tente novamente"
|
||||
ErrDeviceErr: "O ambiente atual não corresponde ao ambiente de importação da licença. Por favor, edite a licença e reimporte"
|
||||
ErrXpackTimeout: "Requisição expirou, a conexão de rede pode estar instável, por favor tente novamente mais tarde"
|
||||
@@ -86,6 +85,19 @@ ErrNodeBind: "Este nó já está vinculado a uma licença, por favor verifique e
|
||||
ErrNodeLocalRollback: "O nó principal não suporta rollback direto. Por favor, execute manualmente o comando '1pctl restore' para fazer o rollback"
|
||||
ErrIntlLicense: "A versão atual ainda não oferece suporte à importação de licença da edição internacional. Em breve"
|
||||
|
||||
# user
|
||||
ErrNoSuchUser: "Informações do usuário não encontradas. Verifique e tente novamente."
|
||||
ErrToMaster: "Este usuário não tem permissão para executar esta operação no nó principal. Verifique e tente novamente."
|
||||
ErrToNode: "Este usuário não tem permissão para executar esta operação neste nó. Verifique e tente novamente."
|
||||
ErrRBAC: "Permissões de usuário insuficientes. Não é possível executar esta operação. Verifique e tente novamente."
|
||||
ErrNoneNode: "Este usuário não tem permissões de nenhum nó. Contate o administrador ou verifique e tente novamente."
|
||||
ErrOnlySuperAdmin: "Somente superadministradores podem usar este recurso."
|
||||
ErrRolePresetCannotDelete: "Funções predefinidas do sistema não podem ser excluídas"
|
||||
ErrRolePresetCannotUpdate: "Funções predefinidas do sistema não podem ser atualizadas"
|
||||
ErrRoleBoundToUser: "Esta função já está vinculada a usuários e não pode ser excluída"
|
||||
ErrRolePresetCannotBind: "Funções predefinidas do sistema não podem ser atribuídas a usuários"
|
||||
ErrSuperAdminCannotDelete: "Usuários superadministradores não podem ser excluídos"
|
||||
|
||||
InvalidRequestBodyType: "Formato do corpo da requisição inválido. Verifique se o conteúdo está no formato correto e tente novamente"
|
||||
InvalidLicenseCodeType: "Formato do código de licença inválido. Verifique e tente novamente"
|
||||
LicenseNotFoundType: "Licença não encontrada. Não há registros correspondentes no sistema para a licença fornecida. Verifique e tente novamente"
|
||||
|
||||
@@ -49,8 +49,6 @@ AppInstallCheck: 'Проверить среду установки прилож
|
||||
|
||||
# backup
|
||||
ErrBackupInUsed: 'Аккаунт бэкапа занят задачей'
|
||||
ErrRolePresetCannotDelete: "System preset roles cannot be deleted"
|
||||
ErrRoleBoundToUser: "This role is already bound to users and cannot be deleted"
|
||||
ErrBackupCheck: 'Проверка подключения не удалась: {{ .err }}'
|
||||
ErrBackupLocal: "Локальная учетная запись резервного копирования не поддерживает эту операцию"
|
||||
ErrBackupPublic: "Обнаружено, что эта учетная запись резервного копирования не является публичной, проверьте и повторите попытку"
|
||||
@@ -69,6 +67,7 @@ ErrLicenseExist: "Данная лицензия уже существует. В
|
||||
ErrXpackNotFound: "Этот раздел доступен в Business Edition. Импортируйте лицензию в Настройки панели > Лицензия"
|
||||
ErrXpackExceptional: "Этот раздел доступен в Business Edition. Синхронизируйте статус лицензии в Настройки панели > Лицензия"
|
||||
ErrXpackLost: "Достигнут лимит повторных попыток по лицензии. Перейдите в Настройки панели > Лицензия и выполните ручную синхронизацию"
|
||||
ErrXpackEELicenseRequired: "Лицензия Enterprise Edition не импортирована. Сначала импортируйте лицензию."
|
||||
ErrDeviceLost: "Необходимые файлы для проверки лицензии отсутствуют, пожалуйста, проверьте и попробуйте снова"
|
||||
ErrDeviceErr: "Текущая среда не соответствует среде импорта лицензии. Отредактируйте лицензию и повторите импорт"
|
||||
ErrXpackTimeout: "Время ожидания запроса истекло, возможно нестабильное сетевое соединение, повторите попытку позже"
|
||||
@@ -86,6 +85,19 @@ ErrNodeBind: "Этот узел уже связан с лицензией, пр
|
||||
ErrNodeLocalRollback: "Прямой откат на основном узле не поддерживается. Выполните '1pctl restore' вручную для отката"
|
||||
ErrIntlLicense: "Текущая версия пока не поддерживает импорт международной лицензии. Скоро будет доступно"
|
||||
|
||||
# user
|
||||
ErrNoSuchUser: "Информация о пользователе не найдена. Проверьте и повторите попытку."
|
||||
ErrToMaster: "У этого пользователя нет прав для выполнения этой операции на основном узле. Проверьте и повторите попытку."
|
||||
ErrToNode: "У этого пользователя нет прав для выполнения этой операции на этом узле. Проверьте и повторите попытку."
|
||||
ErrRBAC: "Недостаточно прав пользователя. Операция не может быть выполнена. Проверьте и повторите попытку."
|
||||
ErrNoneNode: "У этого пользователя нет прав ни на один узел. Обратитесь к администратору или проверьте и повторите попытку."
|
||||
ErrOnlySuperAdmin: "Эта функция доступна только супер администраторам."
|
||||
ErrRolePresetCannotDelete: "Системные предустановленные роли нельзя удалять"
|
||||
ErrRolePresetCannotUpdate: "Системные предустановленные роли нельзя обновлять"
|
||||
ErrRoleBoundToUser: "Эта роль уже привязана к пользователям и не может быть удалена"
|
||||
ErrRolePresetCannotBind: "Системные предустановленные роли нельзя назначать пользователям"
|
||||
ErrSuperAdminCannotDelete: "Пользователей супер-администратора нельзя удалять"
|
||||
|
||||
InvalidRequestBodyType: "Неверный формат тела запроса. Проверьте, соответствует ли содержимое требуемому формату, и повторите попытку"
|
||||
InvalidLicenseCodeType: "Указан неверный формат лицензионного кода. Проверьте и повторите попытку"
|
||||
LicenseNotFoundType: "Лицензия не найдена. В системе нет записей, соответствующих указанной лицензии. Проверьте и повторите попытку"
|
||||
|
||||
@@ -49,8 +49,6 @@ AppInstallCheck: 'Uygulama kurulum ortamını kontrol et'
|
||||
|
||||
# backup
|
||||
ErrBackupInUsed: 'Yedek hesabı görevde kullanılıyor'
|
||||
ErrRolePresetCannotDelete: "System preset roles cannot be deleted"
|
||||
ErrRoleBoundToUser: "This role is already bound to users and cannot be deleted"
|
||||
ErrBackupCheck: 'Bağlantı testi başarısız: {{ .err }}'
|
||||
ErrBackupLocal: "Yerel sunucu yedekleme hesabı bu işlemi desteklemiyor"
|
||||
ErrBackupPublic: "Bu yedekleme hesabının herkese açık olmadığı tespit edildi, lütfen kontrol edip tekrar deneyin"
|
||||
@@ -69,6 +67,7 @@ ErrLicenseExist: "Bu lisans kaydı zaten mevcut. Düğüm bağlama için doğrud
|
||||
ErrXpackNotFound: "Bu bölüm Business Sürüm gerektirir. Lisansı Panel Ayarları > Lisans bölümünden içe aktarın"
|
||||
ErrXpackExceptional: "Bu bölüm Business Sürüm gerektirir. Lisans durumunu Panel Ayarları > Lisans bölümünden senkronize edin"
|
||||
ErrXpackLost: "Lisans yeniden deneme sınırına ulaştı. Panel Ayarları > Lisans bölümüne gidip manuel senkronizasyon çalıştırın"
|
||||
ErrXpackEELicenseRequired: "Enterprise Edition lisansı içe aktarılmamış. Önce bir lisans içe aktarın."
|
||||
ErrDeviceLost: "Lisans doğrulama için gerekli dosyalar eksik, lütfen kontrol edip tekrar deneyin"
|
||||
ErrXpackTimeout: "İstek zaman aşımı, ağ bağlantısı kararsız olabilir, lütfen daha sonra tekrar deneyin"
|
||||
ErrUnbindMaster: "Düğüm yönetiminde düğümler tespit edildi, mevcut lisansın bağı çözülemiyor, lütfen önce düğümleri kaldırın ve tekrar deneyin"
|
||||
@@ -85,6 +84,19 @@ ErrNodeBind: "Bu düğüm zaten bir lisansa bağlı, lütfen kontrol edip tekrar
|
||||
ErrNodeLocalRollback: "Ana düğüm doğrudan rollback'i desteklemez. Rollback için '1pctl restore' komutunu manuel çalıştırın"
|
||||
ErrIntlLicense: "Mevcut sürüm uluslararası sürüm lisansının içe aktarılmasını henüz desteklemiyor. Yakında"
|
||||
|
||||
# user
|
||||
ErrNoSuchUser: "Kullanıcı bilgisi bulunamadı, lütfen kontrol edip tekrar deneyin"
|
||||
ErrToMaster: "Bu kullanıcının ana düğümde bu işlemi yapma izni yok, lütfen kontrol edip tekrar deneyin"
|
||||
ErrToNode: "Bu kullanıcının bu düğümde bu işlemi yapma izni yok, lütfen kontrol edip tekrar deneyin"
|
||||
ErrRBAC: "Kullanıcı izinleri yetersiz, bu işlem yapılamaz. Lütfen kontrol edip tekrar deneyin"
|
||||
ErrNoneNode: "Bu kullanıcının hiçbir düğüm izni yok. Lütfen yöneticiyle iletişime geçin veya kontrol edip tekrar deneyin"
|
||||
ErrOnlySuperAdmin: "Bu özellik yalnızca süper yöneticiler tarafından kullanılabilir"
|
||||
ErrRolePresetCannotDelete: "Sistem ön tanımlı rolleri silinemez"
|
||||
ErrRolePresetCannotUpdate: "Sistem ön tanımlı rolleri güncellenemez"
|
||||
ErrRoleBoundToUser: "Bu rol zaten kullanıcılara bağlı ve silinemez"
|
||||
ErrRolePresetCannotBind: "Sistem ön tanımlı rolleri kullanıcılara atanamaz"
|
||||
ErrSuperAdminCannotDelete: "Süper yönetici kullanıcılar silinemez"
|
||||
|
||||
InvalidRequestBodyType: "Geçersiz istek gövdesi formatı, lütfen içeriğin format gereksinimlerine uygun olduğunu kontrol edip tekrar deneyin"
|
||||
InvalidLicenseCodeType: "Sağlanan lisans kodu formatı geçersiz, lütfen kontrol edip tekrar deneyin"
|
||||
LicenseNotFoundType: "Lisans bulunamadı, sistemde sağlanan lisansla eşleşen bir kayıt yok. Lütfen kontrol edip tekrar deneyin"
|
||||
|
||||
@@ -49,8 +49,6 @@ AppInstallCheck: '檢查應用安裝環境'
|
||||
|
||||
#backup
|
||||
ErrBackupInUsed: "該備份帳號已在排程任務中使用,無法刪除"
|
||||
ErrRolePresetCannotDelete: "系統預設角色,無法刪除"
|
||||
ErrRoleBoundToUser: "角色已被使用者綁定,無法刪除"
|
||||
ErrBackupCheck: "備份帳號測試連線失敗 {{ .err }}"
|
||||
ErrBackupLocal: "本機伺服器備份帳號暫不支援該操作!"
|
||||
ErrBackupPublic: "偵測到該備份帳號為非公用,請檢查後再試。"
|
||||
@@ -69,6 +67,7 @@ ErrLicenseExist: "該許可證記錄已存在,您可直接前往許可證頁
|
||||
ErrXpackNotFound: "此功能需要商業版。請在 面板設定 > 許可證 匯入許可證"
|
||||
ErrXpackExceptional: "此功能需要商業版。請在 面板設定 > 許可證 同步許可證狀態"
|
||||
ErrXpackLost: "許可證已達重試上限,請前往 面板設定 > 許可證 手動同步"
|
||||
ErrXpackEELicenseRequired: "企業版許可證未匯入,請先匯入許可證"
|
||||
ErrDeviceLost: "許可證驗證必要檔案遺失,請檢查後重試。"
|
||||
ErrDeviceErr: "目前環境與許可證匯入環境不一致,請編輯許可證重新匯入!"
|
||||
ErrXpackTimeout: "請求逾時,網路連接可能不穩定,請稍後再試。"
|
||||
@@ -86,6 +85,19 @@ ErrNodeBind: "偵測到該節點已綁定許可證,請檢查後重試。"
|
||||
ErrNodeLocalRollback: "主節點暫不支援直接回滾,請手動執行 '1pctl restore' 進行回滾"
|
||||
ErrIntlLicense: "當前版本暫不支援匯入國際版許可證,敬請期待!"
|
||||
|
||||
# user
|
||||
ErrNoSuchUser: "未能找到該使用者資訊,請檢查後重試!"
|
||||
ErrToMaster: "該使用者無權限對主節點進行此操作,請檢查後重試!"
|
||||
ErrToNode: "該使用者無權限對該節點進行此操作,請檢查後重試!"
|
||||
ErrRBAC: "使用者權限不足,無法進行此操作,請檢查後重試!"
|
||||
ErrNoneNode: "該使用者無任何節點權限,請聯絡管理員或檢查後重試!"
|
||||
ErrOnlySuperAdmin: "僅超級管理員支援此功能!"
|
||||
ErrRolePresetCannotDelete: "系統預設角色,無法刪除"
|
||||
ErrRolePresetCannotUpdate: "系統預設角色,無法修改"
|
||||
ErrRoleBoundToUser: "角色已被使用者綁定,無法刪除"
|
||||
ErrRolePresetCannotBind: "系統預設角色,無法分配給使用者"
|
||||
ErrSuperAdminCannotDelete: "超級管理員使用者無法刪除"
|
||||
|
||||
InvalidRequestBodyType: "請求內容格式錯誤,請檢查請求內容是否符合格式要求後重試!"
|
||||
InvalidLicenseCodeType: "提供的許可證格式錯誤,請檢查後重試。"
|
||||
LicenseNotFoundType: "許可證不存在,系統中未找到與提供許可證符合的紀錄,請檢查後重試。"
|
||||
|
||||
@@ -55,8 +55,6 @@ AppInstallCheck: "检查应用安装环境"
|
||||
|
||||
#backup
|
||||
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
|
||||
ErrRolePresetCannotDelete: "系统预设角色,无法删除"
|
||||
ErrRoleBoundToUser: "角色已被用户绑定,无法删除"
|
||||
ErrBackupCheck: "备份账号测试连接失败 {{ .err }}"
|
||||
ErrBackupLocal: "本地服务器备份账号暂不支持该操作!"
|
||||
ErrBackupPublic: "检测到该备份账号为非公用,请检查后重试!"
|
||||
@@ -72,20 +70,20 @@ ErrXpackVersion: "许可证校验失败,该许可证受版本限制,无法
|
||||
ErrLicenseSave: "许可证信息保存失败,错误 {{ .err }},请重试!"
|
||||
ErrLicenseSync: "许可证信息同步失败,数据库中未检测到许可证信息!"
|
||||
ErrLicenseExist: "该许可证记录已存在,您可直接前往许可证页面进行节点绑定。"
|
||||
ErrXpackNotFound: "该部分为商业版功能,请先在 面板设置-许可证 界面导入许可证"
|
||||
ErrXpackExceptional: "该部分为商业版功能,请先在 面板设置-许可证 界面同步许可证状态"
|
||||
ErrXpackLost: "许可证已达到最大重试次数,请进入【面板设置】【许可证】页面手动点击同步按钮,以确保商业版功能正常使用"
|
||||
ErrXpackNotFound: "该部分为专业版功能,请先在 面板设置-许可证 界面导入许可证"
|
||||
ErrXpackExceptional: "该部分为专业版功能,请先在 面板设置-许可证 界面同步许可证状态"
|
||||
ErrXpackLost: "许可证已达到最大重试次数,请进入【面板设置】【许可证】页面手动点击同步按钮,以确保专业版功能正常使用"
|
||||
ErrXpackEELicenseRequired: "企业版许可证未导入,请先导入许可证"
|
||||
ErrDeviceLost: "许可证校验必要文件丢失,请检查后重试!"
|
||||
ErrDeviceErr: "当前环境与许可证导入环境不一致,请编辑许可证重新导入!"
|
||||
ErrXpackTimeout: "请求超时,网络连接可能不稳定,请稍后再试!"
|
||||
ErrUnbindMaster: "检测到节点管理内存在商业版节点,无法解绑当前许可证,请先移除或解绑后重试!"
|
||||
ErrUnbindMaster: "检测到节点管理内存在专业版节点,无法解绑当前许可证,请先移除或解绑后重试!"
|
||||
ErrFreeNodeLimit: "社区版节点数量已达到免费上限,请前往 www.lxware.cn/1panel 购买后重试!"
|
||||
ErrNodeBound: "该许可证已绑定到其他节点,请检查后重试!"
|
||||
ErrNodeBoundDelete: "该许可证已被绑定,不支持删除操作,请检查后重试!"
|
||||
ErrNodeBoundLimit: "当前免费节点已经达到上限,请检查后重试!"
|
||||
ErrLicenseFree: "仅当许可证正常绑定到节点后,才能使用其免费节点,请检查后重试!"
|
||||
ErrLicenseUnbind: "检测到该许可证存在社区版节点,请在 [ 面板设置 - 许可证 ] 中解绑后重试!"
|
||||
ErrLicenseBindCount: "新许可证免费节点数量小于当前已绑定社区版节点数量,请检查后重试!"
|
||||
ErrNoSuchNode: "未能找到该节点信息,请检查后重试!"
|
||||
ErrNodeUnbind: "检测到该节点未在许可证绑定范围内,请检查后重试!"
|
||||
ErrNodeBind: "检测到该节点已绑定许可证,请检查后重试!"
|
||||
@@ -98,6 +96,12 @@ ErrToMaster: "该用户无权限对主节点进行该操作,请检查后重试
|
||||
ErrToNode: "该用户无权限对该节点进行该操作,请检查后重试!"
|
||||
ErrRBAC: "用户权限不足,无法进行该操作,请检查后重试!"
|
||||
ErrNoneNode: "该用户无任何节点权限,请联系管理员或检查后重试!"
|
||||
ErrOnlySuperAdmin: "仅超级管理员支持该功能!"
|
||||
ErrRolePresetCannotDelete: "系统预设角色,无法删除"
|
||||
ErrRolePresetCannotUpdate: "系统预设角色,无法修改"
|
||||
ErrRoleBoundToUser: "角色已被用户绑定,无法删除"
|
||||
ErrRolePresetCannotBind: "系统预设角色,无法分配给用户"
|
||||
ErrSuperAdminCannotDelete: "超级管理员用户无法删除"
|
||||
|
||||
InvalidRequestBodyType: "请求体格式错误,请检查请求内容是否符合格式要求后重试!"
|
||||
InvalidLicenseCodeType: "提供的许可证格式错误,请检查后重试!"
|
||||
|
||||
@@ -3,8 +3,9 @@ package auth
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ttlstore"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,98 +29,42 @@ type mfaSession struct {
|
||||
}
|
||||
|
||||
type mfaSessionStore struct {
|
||||
mu sync.Mutex
|
||||
items map[string]mfaSession
|
||||
store *ttlstore.Store[mfaSession]
|
||||
}
|
||||
|
||||
func newMFASessionStore() *mfaSessionStore {
|
||||
return &mfaSessionStore{items: make(map[string]mfaSession)}
|
||||
return &mfaSessionStore{
|
||||
store: ttlstore.New[mfaSession](MFASessionTTL, MFASessionStoreMaxEntries, generateMFASessionID),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *mfaSessionStore) Set(name, entrance, ip string) string {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.cleanupExpiredLocked()
|
||||
if len(s.items) >= MFASessionStoreMaxEntries {
|
||||
s.removeOldestLocked()
|
||||
}
|
||||
|
||||
sessionID := generateMFASessionID()
|
||||
s.items[sessionID] = mfaSession{
|
||||
Name: name,
|
||||
Entrance: entrance,
|
||||
IP: ip,
|
||||
ExpiresAt: time.Now().Add(MFASessionTTL),
|
||||
}
|
||||
return sessionID
|
||||
return s.store.Set(mfaSession{
|
||||
Name: name,
|
||||
Entrance: entrance,
|
||||
IP: ip,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *mfaSessionStore) Get(sessionID string) (mfaSession, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
item, ok := s.items[sessionID]
|
||||
if !ok {
|
||||
return mfaSession{}, false
|
||||
}
|
||||
if time.Now().After(item.ExpiresAt) {
|
||||
delete(s.items, sessionID)
|
||||
return mfaSession{}, false
|
||||
}
|
||||
return item, true
|
||||
return s.store.Get(sessionID)
|
||||
}
|
||||
|
||||
func (s *mfaSessionStore) Delete(sessionID string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
delete(s.items, sessionID)
|
||||
s.store.Delete(sessionID)
|
||||
}
|
||||
|
||||
func (s *mfaSessionStore) RecordFailure(sessionID string) int {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
item, ok := s.items[sessionID]
|
||||
failures := 0
|
||||
ok := s.store.Update(sessionID, func(item *mfaSession) bool {
|
||||
item.Failures++
|
||||
failures = item.Failures
|
||||
return item.Failures < MFASessionMaxFailures
|
||||
})
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
if time.Now().After(item.ExpiresAt) {
|
||||
delete(s.items, sessionID)
|
||||
return 0
|
||||
}
|
||||
|
||||
item.Failures++
|
||||
if item.Failures >= MFASessionMaxFailures {
|
||||
delete(s.items, sessionID)
|
||||
return item.Failures
|
||||
}
|
||||
|
||||
s.items[sessionID] = item
|
||||
return item.Failures
|
||||
}
|
||||
|
||||
func (s *mfaSessionStore) cleanupExpiredLocked() {
|
||||
now := time.Now()
|
||||
for id, item := range s.items {
|
||||
if now.After(item.ExpiresAt) {
|
||||
delete(s.items, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *mfaSessionStore) removeOldestLocked() {
|
||||
var oldestID string
|
||||
var oldestTime time.Time
|
||||
for id, item := range s.items {
|
||||
if oldestID == "" || item.ExpiresAt.Before(oldestTime) {
|
||||
oldestID = id
|
||||
oldestTime = item.ExpiresAt
|
||||
}
|
||||
}
|
||||
if oldestID != "" {
|
||||
delete(s.items, oldestID)
|
||||
}
|
||||
return failures
|
||||
}
|
||||
|
||||
func generateMFASessionID() string {
|
||||
|
||||
@@ -56,7 +56,7 @@ func (b *backup) Run() {
|
||||
varsItem, _ := json.Marshal(varMap)
|
||||
_ = global.DB.Model(&model.BackupAccount{}).Where("id = ?", backupItem.ID).Updates(map[string]interface{}{"vars": string(varsItem)}).Error
|
||||
global.LOG.Infof("Refresh %s-%s access_token successful!", backupItem.Type, backupItem.Name)
|
||||
if err := xpack.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
if err := xpack.MultiNodeProvider.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
global.LOG.Errorf("sync backup account to node failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/core/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
@@ -17,12 +18,6 @@ func Init() {
|
||||
global.CONF.Conn.Port, _ = settingRepo.GetValueByKey("ServerPort")
|
||||
global.CONF.Conn.Ipv6, _ = settingRepo.GetValueByKey("Ipv6")
|
||||
global.CONF.Base.Edition, _ = settingRepo.GetValueByKey("Edition")
|
||||
global.Api.ApiInterfaceStatus, _ = settingRepo.GetValueByKey("ApiInterfaceStatus")
|
||||
if global.Api.ApiInterfaceStatus == constant.StatusEnable {
|
||||
global.Api.ApiKey, _ = settingRepo.GetValueByKey("ApiKey")
|
||||
global.Api.IpWhiteList, _ = settingRepo.GetValueByKey("IpWhiteList")
|
||||
global.Api.ApiKeyValidityTime, _ = settingRepo.GetValueByKey("ApiKeyValidityTime")
|
||||
}
|
||||
global.CONF.Conn.BindAddress, _ = settingRepo.GetValueByKey("BindAddress")
|
||||
global.CONF.Conn.SSL, _ = settingRepo.GetValueByKey("SSL")
|
||||
global.CONF.Base.Version, _ = settingRepo.GetValueByKey("SystemVersion")
|
||||
@@ -65,15 +60,24 @@ func handleUserInfo(tags string, settingRepo repo.ISettingRepo) {
|
||||
if strings.Contains(global.CONF.Base.ChangeUserInfo, "entrance") {
|
||||
settingMap["SecurityEntrance"] = common.RandStrAndNum(10)
|
||||
}
|
||||
for key, val := range settingMap {
|
||||
if len(val) == 0 {
|
||||
continue
|
||||
if global.CONF.Base.IsXpackEE {
|
||||
if len(settingMap["UserName"]) != 0 || len(settingMap["Password"]) != 0 {
|
||||
if err := xpack.AuthProvider.ResetSuperAdminUser(settingMap["UserName"], settingMap["Password"]); err != nil {
|
||||
global.LOG.Fatalf("reset xpackee super admin failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if key == "Password" {
|
||||
val, _ = encrypt.StringEncrypt(val)
|
||||
}
|
||||
if err := settingRepo.Update(key, val); err != nil {
|
||||
global.LOG.Errorf("update %s before start failed, err: %v", key, err)
|
||||
} else {
|
||||
for key, val := range settingMap {
|
||||
if len(val) == 0 {
|
||||
continue
|
||||
}
|
||||
if key == "Password" {
|
||||
val, _ = encrypt.StringEncrypt(val)
|
||||
}
|
||||
if err := settingRepo.Update(key, val); err != nil {
|
||||
global.LOG.Errorf("update %s before start failed, err: %v", key, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,37 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func UpsertMenuByLabel(children []dto.ShowMenu, newMenu dto.ShowMenu, afterLabel string) []dto.ShowMenu {
|
||||
for i := range children {
|
||||
if children[i].Label != newMenu.Label {
|
||||
continue
|
||||
}
|
||||
children[i].Disabled = newMenu.Disabled
|
||||
children[i].Title = newMenu.Title
|
||||
children[i].Path = newMenu.Path
|
||||
children[i].Sort = newMenu.Sort
|
||||
children[i].IsShow = newMenu.IsShow
|
||||
return children
|
||||
}
|
||||
|
||||
insertIndex := len(children)
|
||||
for i := range children {
|
||||
if children[i].Label == afterLabel {
|
||||
insertIndex = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
children = append(children, dto.ShowMenu{})
|
||||
copy(children[insertIndex+1:], children[insertIndex:])
|
||||
children[insertIndex] = newMenu
|
||||
return children
|
||||
}
|
||||
|
||||
func LoadMenus() string {
|
||||
item := []dto.ShowMenu{
|
||||
{ID: "1", Disabled: true, Title: "menu.home", IsShow: true, Label: "Home-Menu", Path: "/", Sort: 100},
|
||||
@@ -56,6 +84,23 @@ func LoadMenus() string {
|
||||
{ID: "12", Disabled: false, Title: "menu.logs", IsShow: true, Label: "Log-Menu", Path: "/logs", Sort: 1200},
|
||||
{ID: "13", Disabled: true, Title: "menu.settings", IsShow: true, Label: "Setting-Menu", Path: "/settings", Sort: 1300},
|
||||
}
|
||||
if global.CONF.Base.IsXpackEE {
|
||||
for i := range item {
|
||||
if item[i].Label != "Xpack-Menu" {
|
||||
continue
|
||||
}
|
||||
item[i].Children = UpsertMenuByLabel(item[i].Children, dto.ShowMenu{
|
||||
ID: "121",
|
||||
Disabled: false,
|
||||
Title: "xpack.user.userManage",
|
||||
IsShow: true,
|
||||
Label: "UserManagement",
|
||||
Path: "/xpack-ee/users",
|
||||
Sort: 350,
|
||||
}, "NodeDashboard")
|
||||
break
|
||||
}
|
||||
}
|
||||
menu, _ := json.Marshal(item)
|
||||
return string(menu)
|
||||
}
|
||||
@@ -89,6 +134,8 @@ func MenuSort() []dto.MenuLabelSort {
|
||||
{Label: "XApp", Sort: 100},
|
||||
{Label: "Dashboard", Sort: 200},
|
||||
{Label: "Node", Sort: 300},
|
||||
{Label: "NodeDashboard", Sort: 300},
|
||||
{Label: "UserManagement", Sort: 350},
|
||||
{Label: "Upage", Sort: 400},
|
||||
{Label: "MonitorDashboard", Sort: 500},
|
||||
{Label: "Tamper", Sort: 600},
|
||||
|
||||
@@ -38,6 +38,7 @@ func Init() {
|
||||
migrations.UpdateAiModelMenuStructure,
|
||||
migrations.AddDocSourceSetting,
|
||||
migrations.AddAppStoreInstallAllowPortSetting,
|
||||
migrations.AddUserManagementMenu,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -41,17 +41,19 @@ var InitSetting = &gormigrate.Migration{
|
||||
ID: "20200908-add-table-setting",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
encryptKey := common.RandStr(16)
|
||||
if err := tx.Create(&model.Setting{Key: "UserName", Value: global.CONF.Base.Username}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
global.CONF.Base.EncryptKey = encryptKey
|
||||
pass, _ := encrypt.StringEncrypt(global.CONF.Base.Password)
|
||||
language := "en"
|
||||
if global.CONF.Base.Language == "zh" {
|
||||
language = "zh"
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "Password", Value: pass}).Error; err != nil {
|
||||
return err
|
||||
if !global.CONF.Base.IsXpackEE {
|
||||
if err := tx.Create(&model.Setting{Key: "UserName", Value: global.CONF.Base.Username}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
pass, _ := encrypt.StringEncrypt(global.CONF.Base.Password)
|
||||
if err := tx.Create(&model.Setting{Key: "Password", Value: pass}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, _ = cmd.RunDefaultWithStdoutBashCf("%s sed -i -e 's#ORIGINAL_PASSWORD=.*#ORIGINAL_PASSWORD=**********#g' /usr/local/bin/1pctl", cmd.SudoHandleCmd())
|
||||
if err := tx.Create(&model.Setting{Key: "Theme", Value: "light"}).Error; err != nil {
|
||||
@@ -990,3 +992,52 @@ func normalizeAiMenuChild(children []dto.ShowMenu, fallback dto.ShowMenu, labels
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
var AddUserManagementMenu = &gormigrate.Migration{
|
||||
ID: "20260414-add-user-management-menu",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if !global.CONF.Base.IsXpackEE {
|
||||
return nil
|
||||
}
|
||||
var menuJSON string
|
||||
if err := tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Pluck("value", &menuJSON).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if menuJSON == "" {
|
||||
menuJSON = helper.LoadMenus()
|
||||
}
|
||||
|
||||
var menus []dto.ShowMenu
|
||||
if err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {
|
||||
return tx.Model(&model.Setting{}).
|
||||
Where("key = ?", "HideMenu").
|
||||
Update("value", helper.LoadMenus()).Error
|
||||
}
|
||||
|
||||
newItem := dto.ShowMenu{
|
||||
ID: "121",
|
||||
Disabled: false,
|
||||
Title: "xpack.user.userManage",
|
||||
IsShow: true,
|
||||
Label: "UserManagement",
|
||||
Path: "/xpack-ee/users",
|
||||
Sort: 350,
|
||||
}
|
||||
|
||||
for i := range menus {
|
||||
if menus[i].Label != "Xpack-Menu" {
|
||||
continue
|
||||
}
|
||||
menus[i].Children = helper.UpsertMenuByLabel(menus[i].Children, newItem, "NodeDashboard")
|
||||
break
|
||||
}
|
||||
|
||||
updatedJSON, err := json.Marshal(menus)
|
||||
if err != nil {
|
||||
return tx.Model(&model.Setting{}).
|
||||
Where("key = ?", "HideMenu").
|
||||
Update("value", helper.LoadMenus()).Error
|
||||
}
|
||||
return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,15 +3,14 @@ package router
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/cmd/server/res"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/init/proxy"
|
||||
psessionUtils "github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -58,7 +57,7 @@ func Proxy() gin.HandlerFunc {
|
||||
proxyLocalAgent(c)
|
||||
return
|
||||
}
|
||||
xpack.Proxy(c, currentNode)
|
||||
xpack.MultiNodeProvider.Proxy(c, currentNode)
|
||||
c.Abort()
|
||||
}
|
||||
}
|
||||
@@ -78,18 +77,13 @@ func checkSession(c *gin.Context) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
sessionTimeout, err := settingRepo.GetValueByKey("SessionTimeout")
|
||||
c.Set(psessionUtils.GinContextSessionUserKey, psession)
|
||||
lifeTime, err := xpack.AuthProvider.LoadSessionTimeout(c, psession)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("get session timeout failed, err: %v", err)
|
||||
return false
|
||||
}
|
||||
lifeTime, _ := strconv.Atoi(sessionTimeout)
|
||||
lifeTime = xpack.LoadSessionTimeout(psession, lifeTime)
|
||||
ssl, err := settingRepo.GetValueByKey("SSL")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if _, err := global.SESSION.RefreshIfNeeded(c, psession, ssl == constant.StatusEnable, lifeTime); err != nil {
|
||||
if _, err := global.SESSION.RefreshIfNeeded(c, psession, global.CONF.Conn.SSL == constant.StatusEnable, lifeTime); err != nil {
|
||||
global.LOG.Warnf("proxy refresh session failed, path=%s, err=%v", c.Request.URL.Path, err)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
appauth "github.com/1Panel-dev/1Panel/core/app/auth"
|
||||
"github.com/1Panel-dev/1Panel/core/app/service"
|
||||
"github.com/1Panel-dev/1Panel/core/cmd/server/docs"
|
||||
"github.com/1Panel-dev/1Panel/core/cmd/server/web"
|
||||
@@ -57,7 +58,7 @@ func setWebStatic(rootRouter *gin.RouterGroup) {
|
||||
}
|
||||
entrance = authService.GetSecurityEntrance()
|
||||
if entrance != "" {
|
||||
service.SetSecurityEntranceCookie(c, entrance)
|
||||
appauth.SetSecurityEntranceCookie(c, entrance)
|
||||
}
|
||||
staticServer := http.FileServer(http.FS(web.IndexHtml))
|
||||
staticServer.ServeHTTP(c.Writer, c.Request)
|
||||
@@ -85,10 +86,10 @@ func Routers() *gin.Engine {
|
||||
|
||||
Router.Use(middleware.OperationLog())
|
||||
Router.Use(middleware.GlobalLoading())
|
||||
Router.Use(xpack.AuthProvider.CoreAPIAuthMiddleware())
|
||||
Router.Use(middleware.PasswordExpired())
|
||||
Router.Use(middleware.ApiAuth())
|
||||
Router.Use(middleware.CSRFTokenGuard())
|
||||
Router.Use(xpack.CoreRBACMiddlewares()...)
|
||||
Router.Use(xpack.AuthProvider.CoreRBACMiddlewares()...)
|
||||
Router.Use(Proxy())
|
||||
|
||||
PrivateGroup := Router.Group("/api/v2/core")
|
||||
|
||||
@@ -20,6 +20,7 @@ type SessionUser struct {
|
||||
}
|
||||
|
||||
const SuperAdminSessionUserID = "__super_admin__"
|
||||
const GinContextSessionUserKey = "session_user"
|
||||
|
||||
type sessionItem struct {
|
||||
CreatedAt time.Time
|
||||
@@ -35,7 +36,7 @@ type PSession struct {
|
||||
lastFullCleanup time.Time
|
||||
}
|
||||
|
||||
const maxSessionEntries = 64
|
||||
const maxSessionEntries = 1024
|
||||
|
||||
func NewPSession(_ string) *PSession {
|
||||
return &PSession{
|
||||
|
||||
@@ -2,53 +2,60 @@ package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
psessionUtils "github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
expiredLoc *time.Location
|
||||
expiredLocOnce sync.Once
|
||||
)
|
||||
|
||||
func PasswordExpired() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if strings.HasPrefix(c.Request.URL.Path, "/api/v2/core/auth") ||
|
||||
c.Request.URL.Path == "/api/v2/core/settings/expired/handle" ||
|
||||
c.Request.URL.Path == "/api/v2/core/settings/search" {
|
||||
c.Request.URL.Path == "/api/v2/core/settings/search" ||
|
||||
c.Request.URL.Path == "/api/v2/core/settings/search/base" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
expirationDays, err := settingRepo.GetValueByKey("ExpirationDays")
|
||||
if c.GetBool("API_AUTH") {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
var err error
|
||||
sessionUser, ok := c.Get(psessionUtils.GinContextSessionUserKey)
|
||||
psession, typeOK := sessionUser.(psessionUtils.SessionUser)
|
||||
if !ok || !typeOK {
|
||||
psession, err = global.SESSION.Get(c)
|
||||
}
|
||||
if err != nil {
|
||||
errItem := err.Error()
|
||||
if errItem == "ErrSessionDataFormat" || errItem == "ErrSessionDataNotFound" {
|
||||
helper.BadAuth(c, "ErrNotLogin", buserr.New(errItem))
|
||||
return
|
||||
}
|
||||
helper.BadAuth(c, "ErrNotLogin", err)
|
||||
return
|
||||
}
|
||||
c.Set(psessionUtils.GinContextSessionUserKey, psession)
|
||||
if len(psession.Name) == 0 {
|
||||
helper.BadAuth(c, "ErrNotLogin", err)
|
||||
return
|
||||
}
|
||||
needCheck, expiredTime, err := xpack.AuthProvider.LoadExpired(c, psession)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, http.StatusInternalServerError, "ErrPasswordExpired", err)
|
||||
return
|
||||
}
|
||||
expiredDays, _ := strconv.Atoi(expirationDays)
|
||||
if expiredDays == 0 {
|
||||
if !needCheck {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
expirationTime, err := settingRepo.GetValueByKey("ExpirationTime")
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, http.StatusInternalServerError, "ErrPasswordExpired", err)
|
||||
return
|
||||
}
|
||||
expiredTime, err := time.ParseInLocation(constant.DateTimeLayout, expirationTime, loadExpiredLocation())
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, 313, "ErrPasswordExpired", err)
|
||||
return
|
||||
}
|
||||
if time.Now().After(expiredTime) {
|
||||
helper.ErrorWithDetail(c, 313, "ErrPasswordExpired", err)
|
||||
return
|
||||
@@ -56,18 +63,3 @@ func PasswordExpired() gin.HandlerFunc {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func loadExpiredLocation() *time.Location {
|
||||
expiredLocOnce.Do(func() {
|
||||
loc, err := time.LoadLocation(common.LoadTimeZoneByCmd())
|
||||
if err != nil {
|
||||
expiredLoc = time.Local
|
||||
return
|
||||
}
|
||||
expiredLoc = loc
|
||||
})
|
||||
if expiredLoc == nil {
|
||||
return time.Local
|
||||
}
|
||||
return expiredLoc
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
psessionUtils "github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -16,7 +13,7 @@ import (
|
||||
func SessionAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
apiReq := c.GetBool("API_AUTH")
|
||||
if strings.HasPrefix(c.Request.URL.Path, "/api/v2/core/auth") || apiReq {
|
||||
if isAnonymousAuthPath(c.Request.URL.Path) || apiReq {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
@@ -31,24 +28,20 @@ func SessionAuth() gin.HandlerFunc {
|
||||
helper.BadAuth(c, "ErrNotLogin", err)
|
||||
return
|
||||
}
|
||||
if len(psession.Name) == 0 {
|
||||
if len(psession.Name) == 0 || len(psession.ID) == 0 {
|
||||
helper.BadAuth(c, "ErrNotLogin", err)
|
||||
return
|
||||
}
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
sessionTimeout, err := settingRepo.GetValueByKey("SessionTimeout")
|
||||
c.Set(psessionUtils.GinContextSessionUserKey, psession)
|
||||
lifeTime, err := xpack.AuthProvider.LoadSessionTimeout(c, psession)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("create operation record failed, err: %v", err)
|
||||
global.LOG.Errorf("get session timeout failed, err: %v", err)
|
||||
helper.InternalServer(c, err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
lifeTime, _ := strconv.Atoi(sessionTimeout)
|
||||
lifeTime = xpack.LoadSessionTimeout(psession, lifeTime)
|
||||
ssl, err := settingRepo.GetValueByKey("SSL")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("create operation record failed, err: %v", err)
|
||||
return
|
||||
}
|
||||
if _, err := global.SESSION.RefreshIfNeeded(c, psession, ssl == constant.StatusEnable, lifeTime); err != nil {
|
||||
|
||||
if _, err := global.SESSION.RefreshIfNeeded(c, psession, global.CONF.Conn.SSL == constant.StatusEnable, lifeTime); err != nil {
|
||||
errItem := err.Error()
|
||||
if errItem == "ErrSessionDataFormat" || errItem == "ErrSessionDataNotFound" {
|
||||
helper.BadAuth(c, "ErrNotLogin", buserr.New(errItem))
|
||||
@@ -61,3 +54,19 @@ func SessionAuth() gin.HandlerFunc {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func isAnonymousAuthPath(path string) bool {
|
||||
switch path {
|
||||
case "/api/v2/core/auth/captcha",
|
||||
"/api/v2/core/auth/passkey/begin",
|
||||
"/api/v2/core/auth/passkey/finish",
|
||||
"/api/v2/core/auth/mfalogin",
|
||||
"/api/v2/core/auth/login",
|
||||
"/api/v2/core/auth/logout",
|
||||
"/api/v2/core/auth/setting",
|
||||
"/api/v2/core/auth/welcome":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//go:build xpack
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
xpackRouter "github.com/1Panel-dev/1Panel/core/xpack/router"
|
||||
)
|
||||
|
||||
func RouterGroups() []CommonRouter {
|
||||
baseRouter := commonGroups()
|
||||
for _, ro := range xpackRouter.XpackGroups() {
|
||||
if val, ok := ro.(CommonRouter); ok {
|
||||
baseRouter = append(baseRouter, val)
|
||||
}
|
||||
}
|
||||
return baseRouter
|
||||
}
|
||||
|
||||
var RouterGroupApp = RouterGroups()
|
||||
@@ -1,25 +0,0 @@
|
||||
//go:build xpackee
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
xpackEERouter "github.com/1Panel-dev/1Panel/core/xpack-ee/router"
|
||||
xpackRouter "github.com/1Panel-dev/1Panel/core/xpack/router"
|
||||
)
|
||||
|
||||
func RouterGroups() []CommonRouter {
|
||||
baseRouter := commonGroups()
|
||||
for _, ro := range xpackRouter.XpackGroups() {
|
||||
if val, ok := ro.(CommonRouter); ok {
|
||||
baseRouter = append(baseRouter, val)
|
||||
}
|
||||
}
|
||||
for _, ro := range xpackEERouter.XpackEEGroups() {
|
||||
if val, ok := ro.(CommonRouter); ok {
|
||||
baseRouter = append(baseRouter, val)
|
||||
}
|
||||
}
|
||||
return baseRouter
|
||||
}
|
||||
|
||||
var RouterGroupApp = RouterGroups()
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
v2 "github.com/1Panel-dev/1Panel/core/app/api/v2"
|
||||
"github.com/1Panel-dev/1Panel/core/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -9,6 +10,9 @@ type BaseRouter struct{}
|
||||
|
||||
func (s *BaseRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
baseRouter := Router.Group("auth")
|
||||
authRouter := Router.Group("auth").
|
||||
Use(middleware.SessionAuth()).
|
||||
Use(middleware.PasswordExpired())
|
||||
baseApi := v2.ApiGroupApp.BaseApi
|
||||
{
|
||||
baseRouter.GET("/captcha", baseApi.Captcha)
|
||||
@@ -19,5 +23,17 @@ func (s *BaseRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
baseRouter.POST("/logout", baseApi.LogOut)
|
||||
baseRouter.GET("/setting", baseApi.GetLoginSetting)
|
||||
baseRouter.GET("/welcome", baseApi.GetWelcomePage)
|
||||
|
||||
authRouter.POST("/mfa", baseApi.LoadMFA)
|
||||
authRouter.POST("/mfa/bind", baseApi.MFABind)
|
||||
authRouter.POST("/passkey/register/begin", baseApi.PasskeyRegisterBegin)
|
||||
authRouter.POST("/passkey/register/finish", baseApi.PasskeyRegisterFinish)
|
||||
authRouter.GET("/passkey/list", baseApi.PasskeyList)
|
||||
authRouter.POST("/passkey/del", baseApi.PasskeyDelete)
|
||||
authRouter.POST("/api/generate", baseApi.GenerateApiKey)
|
||||
authRouter.POST("/api/update", baseApi.UpdateApiConfig)
|
||||
|
||||
authRouter.GET("/current", baseApi.GetCurrentUser)
|
||||
authRouter.POST("/current/update", baseApi.UpdateCurrentUser)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
baseApi := v2.ApiGroupApp.BaseApi
|
||||
{
|
||||
router.POST("/search", baseApi.GetSettingInfo)
|
||||
router.POST("/expired/handle", baseApi.HandlePasswordExpired)
|
||||
router.POST("/search/base", baseApi.GetSettingBaseInfo)
|
||||
|
||||
settingRouter.POST("/by", baseApi.GetSettingByKey)
|
||||
settingRouter.POST("/terminal/search", baseApi.GetTerminalSettingInfo)
|
||||
settingRouter.GET("/search/available", baseApi.GetSystemAvailable)
|
||||
@@ -34,20 +35,10 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
settingRouter.POST("/ssl/update", baseApi.UpdateSSL)
|
||||
settingRouter.GET("/ssl/info", baseApi.LoadFromCert)
|
||||
settingRouter.POST("/ssl/download", baseApi.DownloadSSL)
|
||||
settingRouter.POST("/password/update", baseApi.UpdatePassword)
|
||||
settingRouter.POST("/mfa", baseApi.LoadMFA)
|
||||
settingRouter.POST("/mfa/bind", baseApi.MFABind)
|
||||
settingRouter.POST("/passkey/register/begin", baseApi.PasskeyRegisterBegin)
|
||||
settingRouter.POST("/passkey/register/finish", baseApi.PasskeyRegisterFinish)
|
||||
settingRouter.GET("/passkey/list", baseApi.PasskeyList)
|
||||
settingRouter.DELETE("/passkey/:id", baseApi.PasskeyDelete)
|
||||
|
||||
settingRouter.POST("/upgrade", baseApi.Upgrade)
|
||||
settingRouter.POST("/upgrade/notes", baseApi.GetNotesByVersion)
|
||||
settingRouter.GET("/upgrade/releases", baseApi.LoadRelease)
|
||||
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
|
||||
settingRouter.POST("/api/config/generate/key", baseApi.GenerateApiKey)
|
||||
settingRouter.POST("/api/config/update", baseApi.UpdateApiConfig)
|
||||
|
||||
noAuthRouter.POST("/ssl/reload", baseApi.ReloadSSL)
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
//go:build xpack
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
xpack "github.com/1Panel-dev/1Panel/core/xpack"
|
||||
)
|
||||
|
||||
func InitOthers() {
|
||||
xpack.Init()
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
//go:build xpackee
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
xpack "github.com/1Panel-dev/1Panel/core/xpack"
|
||||
xpackEE "github.com/1Panel-dev/1Panel/core/xpack-ee"
|
||||
)
|
||||
|
||||
func InitOthers() {
|
||||
xpack.Init()
|
||||
xpackEE.Init()
|
||||
}
|
||||
@@ -83,7 +83,8 @@ func Start() {
|
||||
type tcpKeepAliveListener struct {
|
||||
*net.TCPListener
|
||||
}
|
||||
if global.CONF.Conn.SSL == constant.StatusEnable {
|
||||
switch global.CONF.Conn.SSL {
|
||||
case constant.StatusEnable:
|
||||
constant.CertStore.Store(loadCert())
|
||||
|
||||
server.TLSConfig = &tls.Config{
|
||||
@@ -96,8 +97,7 @@ func Start() {
|
||||
if err := server.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, "", ""); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
} else if global.CONF.Conn.SSL == constant.StatusMux {
|
||||
case constant.StatusMux:
|
||||
constant.CertStore.Store(loadCert())
|
||||
|
||||
server.TLSConfig = &tls.Config{
|
||||
@@ -144,13 +144,11 @@ func Start() {
|
||||
if err := m.Serve(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
default:
|
||||
global.LOG.Infof("listen at http://%s:%s [%s]", global.CONF.Conn.BindAddress, global.CONF.Conn.Port, tcpItem)
|
||||
if err := server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,5 +214,4 @@ func handleMuxHttpConn(conn net.Conn) {
|
||||
resp.Header.Set("Connection", "close")
|
||||
|
||||
_ = resp.Write(conn)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -54,25 +55,6 @@ func Md5(val string) string {
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
||||
func LoadTimeZoneByCmd() string {
|
||||
loc := time.Now().Location().String()
|
||||
if _, err := time.LoadLocation(loc); err != nil {
|
||||
loc = "Asia/Shanghai"
|
||||
}
|
||||
std, err := cmd.RunDefaultWithStdoutBashC("timedatectl | grep 'Time zone'")
|
||||
if err != nil {
|
||||
return loc
|
||||
}
|
||||
fields := strings.Fields(string(std))
|
||||
if len(fields) != 5 {
|
||||
return loc
|
||||
}
|
||||
if _, err := time.LoadLocation(fields[2]); err != nil {
|
||||
return loc
|
||||
}
|
||||
return fields[2]
|
||||
}
|
||||
|
||||
func ScanPort(port int) bool {
|
||||
ln, err := net.Listen("tcp", ":"+strconv.Itoa(port))
|
||||
if err != nil {
|
||||
@@ -145,6 +127,25 @@ func SplitStr(str string, spi ...string) []string {
|
||||
return results
|
||||
}
|
||||
|
||||
func UniqueUints(items []uint) []uint {
|
||||
result := make([]uint, 0, len(items))
|
||||
seen := make(map[uint]struct{}, len(items))
|
||||
for _, item := range items {
|
||||
if item == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[item]; ok {
|
||||
continue
|
||||
}
|
||||
seen[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i] < result[j]
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func LoadArch() (string, error) {
|
||||
std, err := cmd.RunDefaultWithStdoutBashC("uname -a")
|
||||
if err != nil {
|
||||
|
||||
47
core/utils/common/time.go
Normal file
47
core/utils/common/time.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/utils/cmd"
|
||||
)
|
||||
|
||||
func LoadTimeZoneByCmd() string {
|
||||
loc := time.Now().Location().String()
|
||||
if _, err := time.LoadLocation(loc); err != nil {
|
||||
loc = "Asia/Shanghai"
|
||||
}
|
||||
std, err := cmd.RunDefaultWithStdoutBashC("timedatectl | grep 'Time zone'")
|
||||
if err != nil {
|
||||
return loc
|
||||
}
|
||||
fields := strings.Fields(string(std))
|
||||
if len(fields) != 5 {
|
||||
return loc
|
||||
}
|
||||
if _, err := time.LoadLocation(fields[2]); err != nil {
|
||||
return loc
|
||||
}
|
||||
return fields[2]
|
||||
}
|
||||
|
||||
func LoadExpiredLocation() *time.Location {
|
||||
var (
|
||||
expiredLoc *time.Location
|
||||
expiredLocOnce sync.Once
|
||||
)
|
||||
expiredLocOnce.Do(func() {
|
||||
loc, err := time.LoadLocation(LoadTimeZoneByCmd())
|
||||
if err != nil {
|
||||
expiredLoc = time.Local
|
||||
return
|
||||
}
|
||||
expiredLoc = loc
|
||||
})
|
||||
if expiredLoc == nil {
|
||||
return time.Local
|
||||
}
|
||||
return expiredLoc
|
||||
}
|
||||
@@ -3,22 +3,22 @@ package passkey
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ttlstore"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
)
|
||||
|
||||
const (
|
||||
PasskeyUserIDSettingKey = "PasskeyUserID"
|
||||
PasskeyCredentialSettingKey = "PasskeyCredentials"
|
||||
PasskeyMaxCredentials = 5
|
||||
PasskeySessionTTL = 5 * time.Minute
|
||||
PasskeySessionKindLogin = "login"
|
||||
PasskeySessionKindRegister = "register"
|
||||
PasskeyCredentialNameDefault = "Passkey"
|
||||
PasskeySessionStoreMaxEntries = 1024
|
||||
PasskeyUserIDSettingKey = "PasskeyUserID"
|
||||
PasskeyCredentialSettingKey = "PasskeyCredentials"
|
||||
PasskeyMaxCredentials = 5
|
||||
PasskeySessionTTL = 5 * time.Minute
|
||||
PasskeySessionKindLogin = "login"
|
||||
PasskeySessionKindRegister = "register"
|
||||
PasskeyCredentialNameDefault = "Passkey"
|
||||
PasskeySessionStoreMaxEntries = 1024
|
||||
)
|
||||
|
||||
var passkeySessions = newPasskeySessionStore()
|
||||
@@ -35,75 +35,29 @@ type passkeySession struct {
|
||||
}
|
||||
|
||||
type passkeySessionStore struct {
|
||||
mu sync.Mutex
|
||||
items map[string]passkeySession
|
||||
store *ttlstore.Store[passkeySession]
|
||||
}
|
||||
|
||||
func newPasskeySessionStore() *passkeySessionStore {
|
||||
return &passkeySessionStore{items: make(map[string]passkeySession)}
|
||||
return &passkeySessionStore{
|
||||
store: ttlstore.New[passkeySession](PasskeySessionTTL, PasskeySessionStoreMaxEntries, generatePasskeySessionID),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *passkeySessionStore) Set(kind, name string, session webauthn.SessionData) string {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.cleanupExpiredLocked()
|
||||
if len(s.items) >= PasskeySessionStoreMaxEntries {
|
||||
s.removeOldestLocked()
|
||||
}
|
||||
|
||||
sessionID := generatePasskeySessionID()
|
||||
s.items[sessionID] = passkeySession{
|
||||
Kind: kind,
|
||||
Name: name,
|
||||
Session: session,
|
||||
ExpiresAt: time.Now().Add(PasskeySessionTTL),
|
||||
}
|
||||
return sessionID
|
||||
return s.store.Set(passkeySession{
|
||||
Kind: kind,
|
||||
Name: name,
|
||||
Session: session,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *passkeySessionStore) Get(sessionID string) (passkeySession, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
item, ok := s.items[sessionID]
|
||||
if !ok {
|
||||
return passkeySession{}, false
|
||||
}
|
||||
if time.Now().After(item.ExpiresAt) {
|
||||
delete(s.items, sessionID)
|
||||
return passkeySession{}, false
|
||||
}
|
||||
return item, true
|
||||
return s.store.Get(sessionID)
|
||||
}
|
||||
|
||||
func (s *passkeySessionStore) Delete(sessionID string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
delete(s.items, sessionID)
|
||||
}
|
||||
|
||||
func (s *passkeySessionStore) cleanupExpiredLocked() {
|
||||
now := time.Now()
|
||||
for id, item := range s.items {
|
||||
if now.After(item.ExpiresAt) {
|
||||
delete(s.items, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *passkeySessionStore) removeOldestLocked() {
|
||||
var oldestID string
|
||||
var oldestTime time.Time
|
||||
for id, item := range s.items {
|
||||
if oldestID == "" || item.ExpiresAt.Before(oldestTime) {
|
||||
oldestID = id
|
||||
oldestTime = item.ExpiresAt
|
||||
}
|
||||
}
|
||||
if oldestID != "" {
|
||||
delete(s.items, oldestID)
|
||||
}
|
||||
s.store.Delete(sessionID)
|
||||
}
|
||||
|
||||
func generatePasskeySessionID() string {
|
||||
|
||||
@@ -30,7 +30,7 @@ func HandleRequest(url, method string, timeout int) (int, []byte, error) {
|
||||
}
|
||||
|
||||
func HandleRequestWithProxy(url, method string, timeout int) (int, []byte, error) {
|
||||
transport := xpack.LoadRequestTransport()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
return handleRequestWithTransport(url, method, transport, timeout)
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func HandleGet(url string) (*http.Response, error) {
|
||||
}
|
||||
|
||||
func HandleGetWithProxy(url string) (*http.Response, error) {
|
||||
transport := xpack.LoadRequestTransport()
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
return handleGetWithTransport(url, transport)
|
||||
}
|
||||
|
||||
|
||||
116
core/utils/ttlstore/ttlstore.go
Normal file
116
core/utils/ttlstore/ttlstore.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package ttlstore
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Item[T any] struct {
|
||||
Value T
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
type Store[T any] struct {
|
||||
mu sync.Mutex
|
||||
items map[string]Item[T]
|
||||
ttl time.Duration
|
||||
maxEntries int
|
||||
newID func() string
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func New[T any](ttl time.Duration, maxEntries int, newID func() string) *Store[T] {
|
||||
return &Store[T]{
|
||||
items: make(map[string]Item[T]),
|
||||
ttl: ttl,
|
||||
maxEntries: maxEntries,
|
||||
newID: newID,
|
||||
now: time.Now,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store[T]) Set(value T) string {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.cleanupExpiredLocked()
|
||||
if len(s.items) >= s.maxEntries {
|
||||
s.removeOldestLocked()
|
||||
}
|
||||
|
||||
itemID := s.newID()
|
||||
s.items[itemID] = Item[T]{
|
||||
Value: value,
|
||||
ExpiresAt: s.now().Add(s.ttl),
|
||||
}
|
||||
return itemID
|
||||
}
|
||||
|
||||
func (s *Store[T]) Get(itemID string) (T, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
item, ok := s.items[itemID]
|
||||
if !ok {
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
if s.now().After(item.ExpiresAt) {
|
||||
delete(s.items, itemID)
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
return item.Value, true
|
||||
}
|
||||
|
||||
func (s *Store[T]) Update(itemID string, fn func(*T) bool) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
item, ok := s.items[itemID]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if s.now().After(item.ExpiresAt) {
|
||||
delete(s.items, itemID)
|
||||
return false
|
||||
}
|
||||
|
||||
keep := fn(&item.Value)
|
||||
if !keep {
|
||||
delete(s.items, itemID)
|
||||
return true
|
||||
}
|
||||
|
||||
s.items[itemID] = item
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Store[T]) Delete(itemID string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
delete(s.items, itemID)
|
||||
}
|
||||
|
||||
func (s *Store[T]) cleanupExpiredLocked() {
|
||||
now := s.now()
|
||||
for id, item := range s.items {
|
||||
if now.After(item.ExpiresAt) {
|
||||
delete(s.items, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store[T]) removeOldestLocked() {
|
||||
var oldestID string
|
||||
var oldestTime time.Time
|
||||
for id, item := range s.items {
|
||||
if oldestID == "" || item.ExpiresAt.Before(oldestTime) {
|
||||
oldestID = id
|
||||
oldestTime = item.ExpiresAt
|
||||
}
|
||||
}
|
||||
if oldestID != "" {
|
||||
delete(s.items, oldestID)
|
||||
}
|
||||
}
|
||||
@@ -2,88 +2,8 @@
|
||||
|
||||
package xpack
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
import "github.com/1Panel-dev/1Panel/core/utils/xpack/helper"
|
||||
|
||||
baseDto "github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/init/proxy"
|
||||
"github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ssh"
|
||||
"github.com/1Panel-dev/1Panel/core/xpack/app/model"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
var AuthProvider = helper.NewIAuthProvider()
|
||||
|
||||
func Proxy(c *gin.Context, currentNode string) {
|
||||
if currentNode != "local" && currentNode != "" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := recover(); err != nil && err != http.ErrAbortHandler {
|
||||
global.LOG.Debug(err)
|
||||
}
|
||||
}()
|
||||
proxy.LocalAgentProxy.ServeHTTP(c.Writer, c.Request)
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
func CoreRBACMiddlewares() []gin.HandlerFunc { return nil }
|
||||
|
||||
func ProxyDocker(proxyURL string) error { return nil }
|
||||
|
||||
func UpdateGroup(name string, group, newGroup uint) error { return nil }
|
||||
|
||||
func CheckBackupUsed(name string) error { return nil }
|
||||
|
||||
func LoadRequestTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 60 * time.Second,
|
||||
KeepAlive: 60 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
ResponseHeaderTimeout: 10 * time.Second,
|
||||
IdleConnTimeout: 15 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func LoadNodeInfo(currentNode string) (*ssh.ConnInfo, string, error) {
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
func Sync(dataType string) error { return nil }
|
||||
|
||||
func AutoUpgradeWithMaster() {}
|
||||
|
||||
func Login(_ *gin.Context, _ baseDto.Login, _ string) (*baseDto.UserLoginInfo, string, error) {
|
||||
return nil, "", errors.New("not xpackee build")
|
||||
}
|
||||
|
||||
func LoadSessionTimeout(sessionUser psession.SessionUser, defaultTTL int) int { return defaultTTL }
|
||||
|
||||
func RemoveBindNode(nodeID uint) error { return nil }
|
||||
|
||||
func CheckLicenseStatus(isXpack bool, licenseID, nodeID uint) error {
|
||||
return nil
|
||||
}
|
||||
func LoadLicenseByNodeID(nodeID uint) (bool, model.License, error) {
|
||||
return false, model.License{}, nil
|
||||
}
|
||||
func Bind(node model.Node, license *model.License, withInit, withSync, withDryRun, withDockerRestart bool) error {
|
||||
return nil
|
||||
}
|
||||
func BindFree(node model.Node, licenseID uint) error {
|
||||
return nil
|
||||
}
|
||||
func Unbind(node model.Node, license model.License, withReset, withSync, withDockerRestart bool) error {
|
||||
return nil
|
||||
}
|
||||
func UnbindFree(node model.Node, licenseID uint) error {
|
||||
return nil
|
||||
}
|
||||
var MultiNodeProvider = helper.NewIMultiNodeProvider()
|
||||
|
||||
91
core/utils/xpack/helper/auth_helper.go
Normal file
91
core/utils/xpack/helper/auth_helper.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/auth"
|
||||
baseDto "github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/mfa"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack/providers"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type authHelper struct{}
|
||||
|
||||
func NewIAuthProvider() providers.AuthProvider {
|
||||
return &authHelper{}
|
||||
}
|
||||
|
||||
func (a *authHelper) Login(c *gin.Context, info baseDto.Login, entrance string) (*baseDto.UserLoginInfo, string, error) {
|
||||
return auth.Login(c, info, entrance)
|
||||
}
|
||||
|
||||
func (a *authHelper) MFALogin(c *gin.Context, info baseDto.MFALogin, entrance string) (*baseDto.UserLoginInfo, string, error) {
|
||||
return auth.MFALogin(c, info, entrance)
|
||||
}
|
||||
|
||||
func (a *authHelper) PasskeyBeginLogin(c *gin.Context, entrance string) (*baseDto.PasskeyBeginResponse, string, error) {
|
||||
return auth.PasskeyBeginLogin(c, entrance)
|
||||
}
|
||||
func (a *authHelper) PasskeyFinishLogin(c *gin.Context, sessionID, entrance string) (*baseDto.UserLoginInfo, string, error) {
|
||||
return auth.PasskeyFinishLogin(c, sessionID, entrance)
|
||||
}
|
||||
func (a *authHelper) PasskeyBeginRegister(c *gin.Context, name string) (*baseDto.PasskeyBeginResponse, string, error) {
|
||||
return auth.PasskeyBeginRegister(c, name)
|
||||
}
|
||||
func (a *authHelper) PasskeyFinishRegister(c *gin.Context, sessionID string) (string, error) {
|
||||
return auth.PasskeyFinishRegister(c, sessionID)
|
||||
}
|
||||
func (a *authHelper) PasskeyList(c *gin.Context) ([]baseDto.PasskeyInfo, error) {
|
||||
return auth.PasskeyList()
|
||||
}
|
||||
func (a *authHelper) PasskeyDelete(c *gin.Context, id string) error {
|
||||
return auth.PasskeyDelete(id)
|
||||
}
|
||||
func (a *authHelper) PasskeyStatus(c *gin.Context) bool {
|
||||
return auth.PasskeyStatus(c)
|
||||
}
|
||||
func (a *authHelper) ClearPasskeys() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authHelper) ResetSuperAdminUser(name, password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authHelper) LoadSessionTimeout(_ *gin.Context, sessionUser psession.SessionUser) (int, error) {
|
||||
return auth.LoadSessionTimeout(sessionUser)
|
||||
}
|
||||
func (a *authHelper) LoadExpired(_ *gin.Context, sessionUser psession.SessionUser) (bool, time.Time, error) {
|
||||
return auth.LoadExpired(sessionUser)
|
||||
}
|
||||
|
||||
func (a *authHelper) CoreAPIAuthMiddleware() gin.HandlerFunc {
|
||||
return auth.APIAuthMiddleware(auth.LoadAPIAuthConfig, nil)
|
||||
}
|
||||
|
||||
func (a *authHelper) CoreRBACMiddlewares() []gin.HandlerFunc { return nil }
|
||||
|
||||
func (a *authHelper) LoadMFA(_ *gin.Context, req baseDto.MfaRequest) (mfa.Otp, error) {
|
||||
return auth.LoadMFA(req)
|
||||
}
|
||||
func (a *authHelper) MFABind(_ *gin.Context, req baseDto.MfaCredential) error {
|
||||
return auth.MFABind(req)
|
||||
}
|
||||
func (a *authHelper) GenerateApiKey(_ *gin.Context) (string, error) {
|
||||
return auth.GenerateApiKey()
|
||||
}
|
||||
func (a *authHelper) UpdateApiConfig(c *gin.Context, req baseDto.ApiInterfaceConfig) error {
|
||||
return auth.UpdateApiConfig(req)
|
||||
}
|
||||
|
||||
func (a *authHelper) GetCurrentUserInfo(_ *gin.Context) (*baseDto.CurrentUserInfo, error) {
|
||||
return auth.GetCurrentUserInfo()
|
||||
}
|
||||
func (a *authHelper) UpdateCurrentUserInfo(c *gin.Context, req baseDto.CurrentUserUpdate) error {
|
||||
return auth.UpdateCurrentUserInfo(c, req)
|
||||
}
|
||||
func (a *authHelper) HandlePasswordExpired(c *gin.Context, old, new string) error {
|
||||
return auth.HandlePasswordExpired(c, old, new)
|
||||
}
|
||||
60
core/utils/xpack/helper/multi_node_helper.go
Normal file
60
core/utils/xpack/helper/multi_node_helper.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/init/proxy"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ssh"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type multiNodeHelper struct{}
|
||||
|
||||
func NewIMultiNodeProvider() *multiNodeHelper {
|
||||
return &multiNodeHelper{}
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) Proxy(c *gin.Context, currentNode string) {
|
||||
if currentNode != "local" && currentNode != "" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := recover(); err != nil && err != http.ErrAbortHandler {
|
||||
global.LOG.Debug(err)
|
||||
}
|
||||
}()
|
||||
proxy.LocalAgentProxy.ServeHTTP(c.Writer, c.Request)
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) ProxyDocker(proxyURL string) error { return nil }
|
||||
|
||||
func (m *multiNodeHelper) UpdateGroup(name string, group, newGroup uint) error { return nil }
|
||||
|
||||
func (m *multiNodeHelper) CheckBackupUsed(name string) error { return nil }
|
||||
|
||||
func (m *multiNodeHelper) LoadRequestTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 60 * time.Second,
|
||||
KeepAlive: 60 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
ResponseHeaderTimeout: 10 * time.Second,
|
||||
IdleConnTimeout: 15 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) LoadNodeInfo(currentNode string) (*ssh.ConnInfo, string, error) {
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
func (m *multiNodeHelper) Sync(dataType string) error { return nil }
|
||||
|
||||
func (m *multiNodeHelper) AutoUpgradeWithMaster() {}
|
||||
42
core/utils/xpack/providers/auth.go
Normal file
42
core/utils/xpack/providers/auth.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/mfa"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AuthProvider interface {
|
||||
Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error)
|
||||
MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error)
|
||||
|
||||
ResetSuperAdminUser(name, password string) error
|
||||
|
||||
LoadSessionTimeout(c *gin.Context, sessionUser psession.SessionUser) (int, error)
|
||||
LoadExpired(c *gin.Context, sessionUser psession.SessionUser) (bool, time.Time, error)
|
||||
|
||||
LoadMFA(c *gin.Context, req dto.MfaRequest) (mfa.Otp, error)
|
||||
MFABind(c *gin.Context, req dto.MfaCredential) error
|
||||
|
||||
GenerateApiKey(c *gin.Context) (string, error)
|
||||
UpdateApiConfig(c *gin.Context, req dto.ApiInterfaceConfig) error
|
||||
|
||||
PasskeyBeginLogin(c *gin.Context, entrance string) (*dto.PasskeyBeginResponse, string, error)
|
||||
PasskeyFinishLogin(c *gin.Context, sessionID, entrance string) (*dto.UserLoginInfo, string, error)
|
||||
PasskeyBeginRegister(c *gin.Context, name string) (*dto.PasskeyBeginResponse, string, error)
|
||||
PasskeyFinishRegister(c *gin.Context, sessionID string) (string, error)
|
||||
PasskeyList(c *gin.Context) ([]dto.PasskeyInfo, error)
|
||||
PasskeyDelete(c *gin.Context, id string) error
|
||||
PasskeyStatus(c *gin.Context) bool
|
||||
ClearPasskeys() error
|
||||
|
||||
GetCurrentUserInfo(c *gin.Context) (*dto.CurrentUserInfo, error)
|
||||
UpdateCurrentUserInfo(c *gin.Context, req dto.CurrentUserUpdate) error
|
||||
HandlePasswordExpired(c *gin.Context, old, new string) error
|
||||
|
||||
CoreAPIAuthMiddleware() gin.HandlerFunc
|
||||
CoreRBACMiddlewares() []gin.HandlerFunc
|
||||
}
|
||||
20
core/utils/xpack/providers/multi_node.go
Normal file
20
core/utils/xpack/providers/multi_node.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ssh"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type MultiNodeProvider interface {
|
||||
Proxy(c *gin.Context, currentNode string)
|
||||
ProxyDocker(proxyURL string) error
|
||||
UpdateGroup(name string, group, newGroup uint) error
|
||||
CheckBackupUsed(name string) error
|
||||
LoadNodeInfo(currentNode string) (*ssh.ConnInfo, string, error)
|
||||
Sync(dataType string) error
|
||||
AutoUpgradeWithMaster()
|
||||
|
||||
LoadRequestTransport() *http.Transport
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
//go:build xpack
|
||||
|
||||
package xpack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
baseDto "github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ssh"
|
||||
"github.com/1Panel-dev/1Panel/core/xpack/app/model"
|
||||
edition "github.com/1Panel-dev/1Panel/core/xpack/edition"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Proxy(c *gin.Context, currentNode string) {
|
||||
edition.Proxy(c, currentNode)
|
||||
}
|
||||
|
||||
func CoreRBACMiddlewares() []gin.HandlerFunc { return nil }
|
||||
|
||||
func ProxyDocker(proxyURL string) error { return edition.ProxyDocker(proxyURL) }
|
||||
|
||||
func UpdateGroup(name string, group, newGroup uint) error {
|
||||
return edition.UpdateGroup(name, group, newGroup)
|
||||
}
|
||||
|
||||
func CheckBackupUsed(name string) error {
|
||||
return edition.CheckBackupUsed(name)
|
||||
}
|
||||
|
||||
func LoadRequestTransport() *http.Transport { return edition.LoadRequestTransport() }
|
||||
|
||||
func LoadNodeInfo(currentNode string) (*ssh.ConnInfo, string, error) {
|
||||
return edition.LoadNodeInfo(currentNode)
|
||||
}
|
||||
|
||||
func Sync(dataType string) error { return edition.Sync(dataType) }
|
||||
|
||||
func AutoUpgradeWithMaster() { edition.AutoUpgradeWithMaster() }
|
||||
|
||||
func Login(_ *gin.Context, _ baseDto.Login, _ string) (*baseDto.UserLoginInfo, string, error) {
|
||||
return nil, "", errors.New("not xpackee build")
|
||||
}
|
||||
|
||||
func LoadSessionTimeout(sessionUser psession.SessionUser, defaultTTL int) int { return defaultTTL }
|
||||
|
||||
func RemoveBindNode(nodeID uint) error { return nil }
|
||||
|
||||
func CheckLicenseStatus(isXpack bool, licenseID, nodeID uint) error {
|
||||
return edition.CheckLicenseStatus(isXpack, licenseID, nodeID)
|
||||
}
|
||||
func LoadLicenseByNodeID(nodeID uint) (bool, model.License, error) {
|
||||
return edition.LoadLicenseByNodeID(nodeID)
|
||||
}
|
||||
func Bind(node model.Node, license *model.License, withInit, withSync, withDryRun, withDockerRestart bool) error {
|
||||
return edition.Bind(node, license, withInit, withSync, withDryRun, withDockerRestart)
|
||||
}
|
||||
func BindFree(node model.Node, licenseID uint) error {
|
||||
return edition.BindFree(node, licenseID)
|
||||
}
|
||||
func Unbind(node model.Node, license model.License, withReset, withSync, withDockerRestart bool) error {
|
||||
return edition.Unbind(node, license, withReset, withSync, withDockerRestart)
|
||||
}
|
||||
func UnbindFree(node model.Node, licenseID uint) error {
|
||||
return edition.UnbindFree(node, licenseID)
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
//go:build xpackee
|
||||
|
||||
package xpack
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/init/session/psession"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ssh"
|
||||
edition "github.com/1Panel-dev/1Panel/core/xpack-ee/edition"
|
||||
xeemiddleware "github.com/1Panel-dev/1Panel/core/xpack-ee/router/middleware"
|
||||
"github.com/1Panel-dev/1Panel/core/xpack/app/model"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Proxy(c *gin.Context, currentNode string) {
|
||||
edition.Proxy(c, currentNode)
|
||||
}
|
||||
|
||||
func CoreRBACMiddlewares() []gin.HandlerFunc {
|
||||
return []gin.HandlerFunc{xeemiddleware.RequireRBAC()}
|
||||
}
|
||||
|
||||
func ProxyDocker(proxyURL string) error { return edition.ProxyDocker(proxyURL) }
|
||||
|
||||
func UpdateGroup(name string, group, newGroup uint) error {
|
||||
return edition.UpdateGroup(name, group, newGroup)
|
||||
}
|
||||
|
||||
func CheckBackupUsed(name string) error {
|
||||
return edition.CheckBackupUsed(name)
|
||||
}
|
||||
|
||||
func LoadRequestTransport() *http.Transport { return edition.LoadRequestTransport() }
|
||||
|
||||
func LoadNodeInfo(currentNode string) (*ssh.ConnInfo, string, error) {
|
||||
return edition.LoadNodeInfo(currentNode)
|
||||
}
|
||||
|
||||
func Sync(dataType string) error { return edition.Sync(dataType) }
|
||||
|
||||
func AutoUpgradeWithMaster() { edition.AutoUpgradeWithMaster() }
|
||||
|
||||
func Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error) {
|
||||
return edition.Login(c, info, entrance)
|
||||
}
|
||||
|
||||
func LoadSessionTimeout(sessionUser psession.SessionUser, defaultTTL int) int {
|
||||
return edition.LoadSessionTimeout(sessionUser, defaultTTL)
|
||||
}
|
||||
|
||||
func RemoveBindNode(nodeID uint) error { return nil }
|
||||
|
||||
func CheckLicenseStatus(isXpack bool, licenseID, nodeID uint) error {
|
||||
return edition.CheckLicenseStatus(isXpack, licenseID, nodeID)
|
||||
}
|
||||
func LoadLicenseByNodeID(nodeID uint) (bool, model.License, error) {
|
||||
return edition.LoadLicenseByNodeID(nodeID)
|
||||
}
|
||||
func Bind(node model.Node, license *model.License, withInit, withSync, withDryRun, withDockerRestart bool) error {
|
||||
return edition.Bind(node, license, withInit, withSync, withDryRun, withDockerRestart)
|
||||
}
|
||||
func BindFree(node model.Node, licenseID uint) error {
|
||||
return edition.BindFree(node, licenseID)
|
||||
}
|
||||
func Unbind(node model.Node, license model.License, withReset, withSync, withDockerRestart bool) error {
|
||||
return edition.Unbind(node, license, withReset, withSync, withDockerRestart)
|
||||
}
|
||||
func UnbindFree(node model.Node, licenseID uint) error {
|
||||
return edition.UnbindFree(node, licenseID)
|
||||
}
|
||||
@@ -92,6 +92,11 @@ class RequestHttp {
|
||||
window.location.reload();
|
||||
return Promise.reject(data);
|
||||
}
|
||||
if (data.code == ResultEnum.ERRXPACKEE) {
|
||||
globalStore.isXpackEELicensed = false;
|
||||
router.push({ name: 'XpackEELicenseRequired' });
|
||||
return Promise.reject(data);
|
||||
}
|
||||
if (data.code == ResultEnum.NodeUnBind) {
|
||||
changeToLocal();
|
||||
window.location.reload();
|
||||
|
||||
@@ -47,6 +47,44 @@ export namespace AI {
|
||||
migMode: string;
|
||||
processes: Process[];
|
||||
}
|
||||
export interface MonitorGPUSearch {
|
||||
productName: string;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
}
|
||||
export interface MonitorGPUOptions {
|
||||
gpuType: string;
|
||||
options: Array<string>;
|
||||
chartHide: Array<ChartHide>;
|
||||
}
|
||||
export interface ChartHide {
|
||||
productName: string;
|
||||
process: boolean;
|
||||
gpu: boolean;
|
||||
memory: boolean;
|
||||
power: boolean;
|
||||
temperature: boolean;
|
||||
speed: boolean;
|
||||
}
|
||||
export interface MonitorGPUData {
|
||||
date: Array<Date>;
|
||||
gpuValue: Array<number>;
|
||||
temperatureValue: Array<number>;
|
||||
powerTotal: Array<number>;
|
||||
powerUsed: Array<number>;
|
||||
powerPercent: Array<number>;
|
||||
memoryTotal: Array<number>;
|
||||
memoryUsed: Array<number>;
|
||||
memoryPercent: Array<number>;
|
||||
speedValue: Array<number>;
|
||||
gpuProcesses: Array<Array<GPUProcess>>;
|
||||
}
|
||||
export interface GPUProcess {
|
||||
pid: string;
|
||||
type: string;
|
||||
processName: string;
|
||||
usedMemory: string;
|
||||
}
|
||||
export interface Process {
|
||||
pid: string;
|
||||
type: string;
|
||||
|
||||
@@ -48,15 +48,46 @@ export namespace Login {
|
||||
export interface AuthInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
sessionTimeout: number;
|
||||
expirationDays: number;
|
||||
expirationTime: string;
|
||||
mfaStatus: string;
|
||||
mfaInterval: number;
|
||||
role: string;
|
||||
permissions: string[];
|
||||
nodeScopes: number[];
|
||||
nodeRoles: Array<{ nodeId: number; nodeName: string; roleId: number; roleName: string }>;
|
||||
|
||||
apiInterfaceStatus: string;
|
||||
apiKey: string;
|
||||
ipWhiteList: string;
|
||||
apiKeyValidityTime: number;
|
||||
}
|
||||
export interface AuthInfoUpdate {
|
||||
id: number;
|
||||
name: string;
|
||||
password: string;
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
retryPassword: string;
|
||||
sessionTimeout: number;
|
||||
expirationDays: number;
|
||||
expirationTime: string;
|
||||
}
|
||||
export interface MFARequest {
|
||||
title: string;
|
||||
interval: number;
|
||||
}
|
||||
export interface MFAInfo {
|
||||
secret: string;
|
||||
qrImage: string;
|
||||
}
|
||||
export interface MFABind {
|
||||
secret: string;
|
||||
code: string;
|
||||
interval: string;
|
||||
}
|
||||
export interface ApiConfig {
|
||||
apiInterfaceStatus: string;
|
||||
apiKey: string;
|
||||
ipWhiteList: string;
|
||||
apiKeyValidityTime: number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,45 +160,6 @@ export namespace Host {
|
||||
endTime: Date;
|
||||
}
|
||||
|
||||
export interface MonitorGPUSearch {
|
||||
productName: string;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
}
|
||||
export interface MonitorGPUOptions {
|
||||
gpuType: string;
|
||||
options: Array<string>;
|
||||
chartHide: Array<ChartHide>;
|
||||
}
|
||||
export interface ChartHide {
|
||||
productName: string;
|
||||
process: boolean;
|
||||
gpu: boolean;
|
||||
memory: boolean;
|
||||
power: boolean;
|
||||
temperature: boolean;
|
||||
speed: boolean;
|
||||
}
|
||||
export interface MonitorGPUData {
|
||||
date: Array<Date>;
|
||||
gpuValue: Array<number>;
|
||||
temperatureValue: Array<number>;
|
||||
powerTotal: Array<number>;
|
||||
powerUsed: Array<number>;
|
||||
powerPercent: Array<number>;
|
||||
memoryTotal: Array<number>;
|
||||
memoryUsed: Array<number>;
|
||||
memoryPercent: Array<number>;
|
||||
speedValue: Array<number>;
|
||||
gpuProcesses: Array<Array<GPUProcess>>;
|
||||
}
|
||||
export interface GPUProcess {
|
||||
pid: string;
|
||||
type: string;
|
||||
processName: string;
|
||||
usedMemory: string;
|
||||
}
|
||||
|
||||
export interface SSHInfo {
|
||||
autoStart: boolean;
|
||||
isActive: boolean;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user