Compare commits

..
16 Commits
Author SHA1 Message Date
CityFun 20b89a8fdd fix: update MdEditor component to prevent potential XSS attacks (#11548) 2026-01-04 09:31:43 +00:00
CityFun 8def011e63 fix: update MdEditor component to prevent potential XSS attacks (#11538) 2026-01-04 02:51:41 +00:00
J.Knight 7c5b2f41e3 feat: add support for Porkbun DNS provider (#11233)
* update: ssl add porkbun platform

* fix: some noise
2025-12-08 16:39:27 +08:00
王贺 abd3c24562 fix: update node & go version (#11195) 2025-12-04 23:18:39 +08:00
王贺 f4e64ca8b3 fix: upgrade vite and vue plugin versions (#11193)
fix: upgrade vite and vue plugin versions
2025-12-04 23:10:11 +08:00
王贺 97fde371d9 Update download URLs to use versioned paths (#11192) 2025-12-04 22:10:06 +08:00
ssongliu 85bbb9cb15 fix: Fix partial abnormal issues caused by local backup path modification (#11190) 2025-12-04 22:04:38 +08:00
ssongliu 1d34b4aff7 perf: optimize login API logic (#11187) 2025-12-04 10:06:22 +00:00
ssongliu dc8c50c07a fix: optimize IP whitelist validation logic (#11183) 2025-12-04 17:21:45 +08:00
CityFun 4ac490c6b4 fix: Fixed issue with website config menu lost (#9473) 2025-07-09 07:26:55 +00:00
ssongliu 0d548c7df9 fix: Fix http image registry edit exception (#9460) 2025-07-08 18:18:00 +08:00
ssongliu 8ec5a5bd4b fix: Fix menu internationalization issues (#9456) 2025-07-08 06:05:55 +00:00
ssongliu 2e8bcc09e6 fix: Fix dashboard refresh exception issue (#9455) 2025-07-08 04:15:53 +00:00
ssongliu 29a304e2c8 fix: Optimize container image repo restart detection logic (#9447) 2025-07-08 02:47:53 +00:00
ssongliu b1adc37920 fix: Modify OneDrive default connection settings (#9440) 2025-07-07 10:05:50 +00:00
CityFun 03fac3ec06 fix: Resolve the issue of lost GPU configuration after upgrade ollama (#9362) 2025-07-01 07:47:33 +00:00
46 changed files with 509 additions and 151 deletions
+2 -2
View File
@@ -12,7 +12,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20.2'
node-version: '22.19.0'
- name: Build Web
run: |
cd frontend && npm install && npm run build:pro
@@ -21,7 +21,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
- name: Build Release
uses: goreleaser/goreleaser-action@v6
with:
+2 -2
View File
@@ -12,7 +12,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20.2'
node-version: '22.19.0'
- name: Build Web
run: |
cd frontend && npm install && npm run build:pro
@@ -21,7 +21,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
- name: Build Release
uses: goreleaser/goreleaser-action@v6
with:
+1 -1
View File
@@ -31,4 +31,4 @@ build_backend_on_darwin:
build_all: build_frontend build_backend_on_linux
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
build_on_local: clean_assets build_frontend build_backend_on_darwin
+17 -6
View File
@@ -2,12 +2,14 @@ package v1
import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/captcha"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/gin-gonic/gin"
)
@@ -26,7 +28,9 @@ func (b *BaseApi) Login(c *gin.Context) {
return
}
if req.AuthMethod != "jwt" && !req.IgnoreCaptcha {
ip := common.GetRealClientIP(c)
needCaptcha := global.IPTracker.NeedCaptcha(ip)
if needCaptcha {
if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -48,9 +52,11 @@ func (b *BaseApi) Login(c *gin.Context) {
user, err := authService.Login(c, req, string(entrance))
go saveLoginLogs(c, err)
if err != nil {
global.IPTracker.SetNeedCaptcha(ip)
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
global.IPTracker.Clear(ip)
helper.SuccessWithData(c, user)
}
@@ -134,16 +140,21 @@ func (b *BaseApi) CheckIsIntl(c *gin.Context) {
}
// @Tags Auth
// @Summary Load System Language
// @Success 200 {string} language
// @Router /auth/language [get]
func (b *BaseApi) GetLanguage(c *gin.Context) {
// @Summary Load System Setting for login
// @Success 200 {object} dto.LoginSetting
// @Router /auth/setting [get]
func (b *BaseApi) GetAuthSetting(c *gin.Context) {
settingInfo, err := settingService.GetSettingInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, settingInfo.Language)
ip := common.GetRealClientIP(c)
needCaptcha := global.IPTracker.NeedCaptcha(ip)
helper.SuccessWithData(c, dto.LoginSetting{
NeedCaptcha: needCaptcha,
Language: settingInfo.Language,
})
}
func saveLoginLogs(c *gin.Context, err error) {
+5 -5
View File
@@ -76,7 +76,7 @@ func (b *BaseApi) CheckRepoStatus(c *gin.Context) {
// @Tags Container Image-repo
// @Summary Create image repo
// @Accept json
// @Param request body dto.ImageRepoDelete true "request"
// @Param request body dto.ImageRepoCreate true "request"
// @Produce json
// @Success 200
// @Security ApiKeyAuth
@@ -99,20 +99,20 @@ func (b *BaseApi) CreateRepo(c *gin.Context) {
// @Tags Container Image-repo
// @Summary Delete image repo
// @Accept json
// @Param request body dto.ImageRepoDelete true "request"
// @Param request body dto.OperateByID true "request"
// @Produce json
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/repo/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"image_repos","output_column":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"}
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_column":"name","output_value":"name"}],"formatZH":"删除镜像仓库 [name]","formatEN":"delete image repo [name]"}
func (b *BaseApi) DeleteRepo(c *gin.Context) {
var req dto.ImageRepoDelete
var req dto.OperateByID
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := imageRepoService.BatchDelete(req); err != nil {
if err := imageRepoService.Delete(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
+11 -7
View File
@@ -23,13 +23,12 @@ type MfaCredential struct {
}
type Login struct {
Name string `json:"name" validate:"required"`
Password string `json:"password" validate:"required"`
IgnoreCaptcha bool `json:"ignoreCaptcha"`
Captcha string `json:"captcha"`
CaptchaID string `json:"captchaID"`
AuthMethod string `json:"authMethod" validate:"required,oneof=jwt session"`
Language string `json:"language" validate:"required,oneof=zh en tw ja ko ru ms 'pt-BR'"`
Name string `json:"name" validate:"required"`
Password string `json:"password" validate:"required"`
Captcha string `json:"captcha"`
CaptchaID string `json:"captchaID"`
AuthMethod string `json:"authMethod" validate:"required,oneof=jwt session"`
Language string `json:"language" validate:"required,oneof=zh en tw ja ko ru ms 'pt-BR'"`
}
type MFALogin struct {
@@ -38,3 +37,8 @@ type MFALogin struct {
Code string `json:"code" validate:"required"`
AuthMethod string `json:"authMethod"`
}
type LoginSetting struct {
NeedCaptcha bool `json:"needCaptcha"`
Language string `json:"language"`
}
+31 -6
View File
@@ -442,17 +442,14 @@ func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, f
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
}
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
if detail.DockerCompose == "" {
return "", nil
}
func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map[string]interface{}, error) {
composeMap := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); err != nil {
return "", err
return nil, err
}
value, ok := composeMap["services"]
if !ok || value == nil {
return "", buserr.New(constant.ErrFileParse)
return nil, buserr.New("ErrFileParse")
}
servicesMap := value.(map[string]interface{})
if len(servicesMap) == 1 {
@@ -470,6 +467,34 @@ func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string
delete(servicesMap, oldServiceName)
}
}
serviceValue := servicesMap[install.ServiceName].(map[string]interface{})
oldComposeMap := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(install.DockerCompose), &oldComposeMap); err != nil {
return nil, err
}
oldValue, ok := oldComposeMap["services"]
if !ok || oldValue == nil {
return nil, buserr.New("ErrFileParse")
}
oldValueMap := oldValue.(map[string]interface{})
oldServiceValue := oldValueMap[install.ServiceName].(map[string]interface{})
if oldServiceValue["deploy"] != nil {
serviceValue["deploy"] = oldServiceValue["deploy"]
}
servicesMap[install.ServiceName] = serviceValue
composeMap["services"] = servicesMap
return composeMap, nil
}
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
if detail.DockerCompose == "" {
return "", nil
}
composeMap, err := handleUpgradeCompose(install, detail)
if err != nil {
return "", err
}
envs := make(map[string]interface{})
if err := json.Unmarshal([]byte(install.Env), &envs); err != nil {
return "", err
+43 -2
View File
@@ -413,9 +413,9 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
if strings.HasSuffix(dirStr, "/") && dirStr != "/" {
dirStr = dirStr[:strings.LastIndex(dirStr, "/")]
}
if err := copyDir(oldDir, dirStr); err != nil {
if err := changeLocalBackup(oldDir, dirStr); err != nil {
_ = backupRepo.Update(req.ID, map[string]interface{}{"vars": oldVars})
return err
return fmt.Errorf("copy dir from %s to %s failed, err: %v", oldDir, dirStr, err)
}
global.CONF.System.Backup = dirStr
}
@@ -677,3 +677,44 @@ func (u *BackupService) Run() {
}).Error
global.LOG.Info("Successfully refreshed OneDrive token.")
}
func changeLocalBackup(oldPath, newPath string) error {
fileOp := fileUtils.NewFileOp()
if fileOp.Stat(path.Join(oldPath, "app")) {
if err := fileOp.CopyDir(path.Join(oldPath, "app"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "database")) {
if err := fileOp.CopyDir(path.Join(oldPath, "database"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "directory")) {
if err := fileOp.CopyDir(path.Join(oldPath, "directory"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "system_snapshot")) {
if err := fileOp.CopyDir(path.Join(oldPath, "system_snapshot"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "website")) {
if err := fileOp.CopyDir(path.Join(oldPath, "website"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "log")) {
if err := fileOp.CopyDir(path.Join(oldPath, "log"), newPath); err != nil {
return err
}
}
_ = fileOp.RmRf(path.Join(oldPath, "app"))
_ = fileOp.RmRf(path.Join(oldPath, "database"))
_ = fileOp.RmRf(path.Join(oldPath, "directory"))
_ = fileOp.RmRf(path.Join(oldPath, "system_snapshot"))
_ = fileOp.RmRf(path.Join(oldPath, "website"))
_ = fileOp.RmRf(path.Join(oldPath, "log"))
return nil
}
+73 -47
View File
@@ -14,7 +14,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
@@ -27,7 +26,7 @@ type IImageRepoService interface {
Login(req dto.OperateByID) error
Create(req dto.ImageRepoCreate) error
Update(req dto.ImageRepoUpdate) error
BatchDelete(req dto.ImageRepoDelete) error
Delete(req dto.OperateByID) error
}
func NewIImageRepoService() IImageRepoService {
@@ -90,30 +89,7 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
}
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
}
ticker := time.NewTicker(3 * time.Second)
defer ticker.Stop()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
if err := func() error {
for range ticker.C {
select {
case <-ctx.Done():
cancel()
return errors.New("the docker service cannot be restarted")
default:
if ok, _ := systemctl.IsActive("docker"); ok {
global.LOG.Info("docker restart with new conf successful!")
return nil
}
}
}
return nil
}(); err != nil {
if err := stopBeforeUpdateRepo(); err != nil {
return err
}
}
@@ -131,15 +107,32 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
return imageRepoRepo.Create(&imageRepo)
}
func (u *ImageRepoService) BatchDelete(req dto.ImageRepoDelete) error {
for _, id := range req.Ids {
if id == 1 {
return errors.New("The default value cannot be edit !")
}
func (u *ImageRepoService) Delete(req dto.OperateByID) error {
if req.ID == 1 {
return errors.New("The default value cannot be edit !")
}
if err := imageRepoRepo.Delete(commonRepo.WithIdsIn(req.Ids)); err != nil {
itemRepo, _ := imageRepoRepo.Get(commonRepo.WithByID(req.ID))
if itemRepo.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
if itemRepo.Auth {
_, _ = cmd.Execf("docker logout -i %s", itemRepo.DownloadUrl)
}
if itemRepo.Protocol == "https" {
return imageRepoRepo.Delete(commonRepo.WithByID(req.ID))
}
if err := u.handleRegistries("", itemRepo.DownloadUrl, "delete"); err != nil {
return fmt.Errorf("delete registry %s failed, err: %v", itemRepo.DownloadUrl, err)
}
if err := validateDockerConfig(); err != nil {
return err
}
if err := imageRepoRepo.Delete(commonRepo.WithByID(req.ID)); err != nil {
return err
}
go func() {
_ = restartDocker()
}()
return nil
}
@@ -154,37 +147,39 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
if err != nil {
return err
}
needRestart := false
if repo.Protocol == "http" && req.Protocol == "https" {
if err := u.handleRegistries("", repo.DownloadUrl, "delete"); err != nil {
return fmt.Errorf("delete registry %s failed, err: %v", repo.DownloadUrl, err)
}
needRestart = true
}
if repo.Protocol == "http" && req.Protocol == "http" {
if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil {
return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err)
if repo.DownloadUrl != req.DownloadUrl {
if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil {
return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err)
}
needRestart = true
}
}
if repo.Protocol == "https" && req.Protocol == "http" {
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
}
needRestart = true
}
if repo.Auth != req.Auth || repo.DownloadUrl != req.DownloadUrl {
if repo.Auth {
_, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl)
}
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
return err
}
if needRestart {
if err := stopBeforeUpdateRepo(); err != nil {
return err
}
}
if err := validateDockerConfig(); err != nil {
return err
if repo.Auth {
_, _ = cmd.Execf("docker logout -i %s", repo.DownloadUrl)
}
if err := restartDocker(); err != nil {
return err
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
return err
}
}
upMap := make(map[string]interface{})
@@ -256,3 +251,34 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
}
return nil
}
func stopBeforeUpdateRepo() error {
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
}
ticker := time.NewTicker(3 * time.Second)
defer ticker.Stop()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
if err := func() error {
for range ticker.C {
select {
case <-ctx.Done():
cancel()
return errors.New("the docker service cannot be restarted")
default:
stdout, err := cmd.Exec("systemctl is-active docker")
if string(stdout) == "active\n" && err == nil {
global.LOG.Info("docker restart with new conf successful!")
return nil
}
}
}
return nil
}(); err != nil {
return err
}
return nil
}
+3
View File
@@ -2,6 +2,7 @@ package global
import (
"github.com/1Panel-dev/1Panel/backend/configs"
"github.com/1Panel-dev/1Panel/backend/init/auth"
"github.com/1Panel-dev/1Panel/backend/init/cache/badger_db"
"github.com/1Panel-dev/1Panel/backend/init/session/psession"
"github.com/dgraph-io/badger/v4"
@@ -28,6 +29,8 @@ var (
MonitorCronID cron.EntryID
OneDriveCronID cron.EntryID
IPTracker *auth.IPTracker
I18n *i18n.Localizer
I18nForCmd *i18n.Localizer
)
+99
View File
@@ -0,0 +1,99 @@
package auth
import (
"sync"
"time"
)
const (
MaxIPCount = 100
ExpireDuration = 30 * time.Minute
)
type IPRecord struct {
NeedCaptcha bool
LastUpdate time.Time
}
type IPTracker struct {
records map[string]*IPRecord
ipOrder []string
mu sync.RWMutex
}
func NewIPTracker() *IPTracker {
return &IPTracker{
records: make(map[string]*IPRecord),
ipOrder: make([]string, 0),
}
}
func (t *IPTracker) NeedCaptcha(ip string) bool {
t.mu.Lock()
defer t.mu.Unlock()
record, exists := t.records[ip]
if !exists {
return false
}
if time.Since(record.LastUpdate) > ExpireDuration {
t.removeIPUnsafe(ip)
return false
}
return record.NeedCaptcha
}
func (t *IPTracker) SetNeedCaptcha(ip string) {
t.mu.Lock()
defer t.mu.Unlock()
if record, exists := t.records[ip]; exists {
if time.Since(record.LastUpdate) > ExpireDuration {
t.removeIPUnsafe(ip)
} else {
record.NeedCaptcha = true
record.LastUpdate = time.Now()
return
}
}
if len(t.records) >= MaxIPCount {
t.removeOldestUnsafe()
}
t.records[ip] = &IPRecord{
NeedCaptcha: true,
LastUpdate: time.Now(),
}
t.ipOrder = append(t.ipOrder, ip)
}
func (t *IPTracker) Clear(ip string) {
t.mu.Lock()
defer t.mu.Unlock()
t.removeIPUnsafe(ip)
}
func (t *IPTracker) removeIPUnsafe(ip string) {
delete(t.records, ip)
for i, storedIP := range t.ipOrder {
if storedIP == ip {
t.ipOrder = append(t.ipOrder[:i], t.ipOrder[i+1:]...)
break
}
}
}
func (t *IPTracker) removeOldestUnsafe() {
if len(t.ipOrder) == 0 {
return
}
oldestIP := t.ipOrder[0]
delete(t.records, oldestIP)
t.ipOrder = t.ipOrder[1:]
}
+2
View File
@@ -109,6 +109,8 @@ func Init() {
migrations.AddMcpServer,
migrations.AddPbootCMSPHPExtensions,
migrations.DeleteV2Openresty,
migrations.UpdateOnedrive,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
@@ -485,3 +485,27 @@ var AddPbootCMSPHPExtensions = &gormigrate.Migration{
return nil
},
}
var DeleteV2Openresty = &gormigrate.Migration{
ID: "20250701-delete-v2-openresty",
Migrate: func(tx *gorm.DB) error {
return tx.Where("version = '1.27.1.2-0-1-focal'").Delete(&model.AppDetail{}).Error
},
}
var UpdateOnedrive = &gormigrate.Migration{
ID: "20250704-update-onedrive",
Migrate: func(tx *gorm.DB) error {
if err := tx.Model(&model.Setting{}).
Where("key = ?", "OneDriveID").
Update("value", "NTQ0NmNmZTMtNGM3OS00N2EwLWFlMjUtZmM2NDU0NzhlMmQ5").Error; err != nil {
return err
}
if err := tx.Model(&model.Setting{}).
Where("key = ?", "OneDriveSc").
Update("value", "bGRlOFF+WEVrR1M0b25Vb1VsRWpMYzE2MW9rTXZEM25KdnZ1MGN6MA==").Error; err != nil {
return err
}
return nil
},
}
+5 -4
View File
@@ -3,14 +3,15 @@ package router
import (
"encoding/base64"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/cmd/server/res"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/cmd/server/res"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/middleware"
@@ -160,7 +161,7 @@ func setWebStatic(rootRouter *gin.RouterGroup) {
}
func Routers() *gin.Engine {
Router = gin.Default()
Router = gin.New()
Router.Use(middleware.OperationLog())
// Router.Use(middleware.CSRF())
// Router.Use(middleware.LoadCsrfToken())
+6 -1
View File
@@ -8,11 +8,17 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/gin-gonic/gin"
)
func WhiteAllow() gin.HandlerFunc {
return func(c *gin.Context) {
clientIP := common.GetRealClientIP(c)
if common.IsPrivateIP(clientIP) {
c.Next()
return
}
settingRepo := repo.NewISettingRepo()
status, err := settingRepo.Get(settingRepo.WithByKey("AllowIPs"))
if err != nil {
@@ -24,7 +30,6 @@ func WhiteAllow() gin.HandlerFunc {
c.Next()
return
}
clientIP := c.ClientIP()
for _, ip := range strings.Split(status.Value, ",") {
if len(ip) == 0 {
continue
+1 -1
View File
@@ -16,7 +16,7 @@ func (s *BaseRouter) InitRouter(Router *gin.RouterGroup) {
baseRouter.POST("/login", baseApi.Login)
baseRouter.POST("/logout", baseApi.LogOut)
baseRouter.GET("/demo", baseApi.CheckIsDemo)
baseRouter.GET("/language", baseApi.GetLanguage)
baseRouter.GET("/setting", baseApi.GetAuthSetting)
baseRouter.GET("/intl", baseApi.CheckIsIntl)
}
}
+2
View File
@@ -13,6 +13,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/init/app"
"github.com/1Panel-dev/1Panel/backend/init/auth"
"github.com/1Panel-dev/1Panel/backend/init/business"
"github.com/1Panel-dev/1Panel/backend/init/lang"
@@ -52,6 +53,7 @@ func Start() {
business.Init()
rootRouter := router.Routers()
global.IPTracker = auth.NewIPTracker()
tcpItem := "tcp4"
if global.CONF.System.Ipv6 == "enable" {
+3 -3
View File
@@ -11,13 +11,13 @@ import (
var store = base64Captcha.DefaultMemStore
func VerifyCode(codeID string, code string) error {
if codeID == "" {
return constant.ErrCaptchaCode
}
vv := store.Get(codeID, true)
vv = strings.TrimSpace(vv)
code = strings.TrimSpace(code)
if codeID == "" || code == "" {
return constant.ErrCaptchaCode
}
if strings.EqualFold(vv, code) {
return nil
}
+16
View File
@@ -426,3 +426,19 @@ func HandleIPList(content string) ([]string, error) {
}
return res, nil
}
func GetRealClientIP(c *gin.Context) string {
addr := c.Request.RemoteAddr
if ip, _, err := net.SplitHostPort(addr); err == nil {
return ip
}
return addr
}
func IsPrivateIP(ipStr string) bool {
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
return ip.IsPrivate() || ip.IsLoopback()
}
+13 -4
View File
@@ -3,14 +3,10 @@ package ssl
import (
"crypto"
"encoding/json"
"github.com/go-acme/lego/v4/providers/dns/westcn"
"os"
"strings"
"time"
"github.com/go-acme/lego/v4/providers/dns/freemyip"
"github.com/go-acme/lego/v4/providers/dns/rainyun"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
@@ -23,13 +19,17 @@ import (
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
"github.com/go-acme/lego/v4/providers/dns/cloudns"
"github.com/go-acme/lego/v4/providers/dns/dnspod"
"github.com/go-acme/lego/v4/providers/dns/freemyip"
"github.com/go-acme/lego/v4/providers/dns/godaddy"
"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
"github.com/go-acme/lego/v4/providers/dns/namecheap"
"github.com/go-acme/lego/v4/providers/dns/namedotcom"
"github.com/go-acme/lego/v4/providers/dns/namesilo"
"github.com/go-acme/lego/v4/providers/dns/porkbun"
"github.com/go-acme/lego/v4/providers/dns/rainyun"
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
"github.com/go-acme/lego/v4/providers/dns/volcengine"
"github.com/go-acme/lego/v4/providers/dns/westcn"
"github.com/go-acme/lego/v4/providers/http/webroot"
"github.com/go-acme/lego/v4/registration"
"github.com/pkg/errors"
@@ -87,6 +87,7 @@ const (
HuaweiCloud DnsType = "HuaweiCloud"
RainYun DnsType = "RainYun"
WestCN DnsType = "WestCN"
PorkBun DnsType = "PorkBun"
)
type DNSParam struct {
@@ -244,6 +245,14 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.Web
westcnConfig.PollingInterval = pollingInterval
westcnConfig.TTL = ttl
p, err = westcn.NewDNSProviderConfig(westcnConfig)
case PorkBun:
porkbunConfig := porkbun.NewDefaultConfig()
porkbunConfig.APIKey = param.APIkey
porkbunConfig.SecretAPIKey = param.SecretKey
porkbunConfig.PropagationTimeout = propagationTimeout
porkbunConfig.PollingInterval = pollingInterval
porkbunConfig.TTL = ttl
p, err = porkbun.NewDNSProviderConfig(porkbunConfig)
}
if err != nil {
return err
+5 -5
View File
@@ -6,18 +6,18 @@ command -v wget >/dev/null || {
}
if [ ! -f "1pctl" ]; then
wget https://github.com/1Panel-dev/installer/raw/main/1pctl
wget https://github.com/1Panel-dev/installer/raw/v1/1pctl
fi
if [ ! -f "install.sh" ]; then
wget https://github.com/1Panel-dev/installer/raw/main/install.sh
wget https://github.com/1Panel-dev/installer/raw/v1/install.sh
fi
if [ ! -d "initscript" ]; then
wget https://github.com/1Panel-dev/installer/raw/main/initscript/1panel.service
wget https://github.com/1Panel-dev/installer/raw/v1/initscript/1panel.service
mkdir -p initscript && cd initscript
for file in 1panel.service 1paneld.init 1paneld.openrc 1paneld.procd; do
wget -q https://github.com/1Panel-dev/installer/raw/main/initscript/$file
wget -q https://github.com/1Panel-dev/installer/raw/v1/initscript/$file
done
cd ..
fi
@@ -25,7 +25,7 @@ fi
if [ ! -d "lang" ]; then
mkdir -p lang && cd lang
for lang in en fa pt-BR ru zh; do
wget -q https://github.com/1Panel-dev/installer/raw/main/lang/$lang.sh
wget -q https://github.com/1Panel-dev/installer/raw/v1/lang/$lang.sh
done
cd ..
fi
+5 -4
View File
@@ -38,8 +38,9 @@
"axios": "^1.7.2",
"codemirror": "^6.0.1",
"crypto-js": "^4.2.0",
"dompurify": "^3.3.1",
"echarts": "^5.5.0",
"element-plus": "^2.7.5",
"element-plus": "2.9.9",
"fit2cloud-ui-plus": "^1.2.0",
"highlight.js": "^11.9.0",
"js-base64": "^3.7.7",
@@ -64,8 +65,8 @@
"@types/node": "^20.15.0",
"@typescript-eslint/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^5.22.0",
"@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vitejs/plugin-vue-jsx": "^5.1.1",
"autoprefixer": "^10.4.7",
"commitizen": "^4.2.4",
"eslint": "^8.43.0",
@@ -84,7 +85,7 @@
"typescript": "^4.5.4",
"unplugin-auto-import": "^0.16.4",
"unplugin-vue-components": "^0.25.0",
"vite": "^6.0.7",
"vite": "^7.1.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-html": "^3.2.2",
+4 -1
View File
@@ -2,7 +2,6 @@ export namespace Login {
export interface ReqLoginForm {
name: string;
password: string;
ignoreCaptcha: boolean;
captcha: string;
captchaID: string;
authMethod: string;
@@ -26,4 +25,8 @@ export namespace Login {
export interface ResAuthButtons {
[propName: string]: any;
}
export interface LoginSetting {
language: string;
needCaptcha: boolean;
}
}
-3
View File
@@ -212,9 +212,6 @@ export namespace Container {
password: string;
auth: boolean;
}
export interface RepoDelete {
ids: Array<number>;
}
export interface RepoInfo {
id: number;
createdAt: Date;
+2 -2
View File
@@ -21,8 +21,8 @@ export const checkIsDemo = () => {
return http.get<boolean>('/auth/demo');
};
export const getLanguage = () => {
return http.get<string>(`/auth/language`);
export const getAuthSetting = () => {
return http.get<Login.LoginSetting>(`/auth/setting`);
};
export const checkIsIntl = () => {
+2 -2
View File
@@ -135,8 +135,8 @@ export const createImageRepo = (params: Container.RepoCreate) => {
export const updateImageRepo = (params: Container.RepoUpdate) => {
return http.post(`/containers/repo/update`, params, TimeoutEnum.T_40S);
};
export const deleteImageRepo = (params: Container.RepoDelete) => {
return http.post(`/containers/repo/del`, params, TimeoutEnum.T_40S);
export const deleteImageRepo = (id: Number) => {
return http.post(`/containers/repo/del`, { id: id }, TimeoutEnum.T_40S);
};
// composeTemplate
@@ -0,0 +1,24 @@
<template>
<MdEditor previewOnly v-model="sanitizedReadMe" :theme="isDarkTheme ? 'dark' : 'light'" />
</template>
<script lang="ts" setup>
import MdEditor from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import DOMPurify from 'dompurify';
import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
import { computed } from 'vue';
const globalStore = GlobalStore();
const { isDarkTheme } = storeToRefs(globalStore);
const props = defineProps({
content: {
type: String,
default: '',
},
});
const sanitizedReadMe = computed(() => {
return DOMPurify.sanitize(props.content);
});
</script>
@@ -69,7 +69,7 @@
{{ upgradeInfo.testVersion }}
</el-radio>
</el-radio-group>
<MdEditor v-model="upgradeInfo.releaseNote" previewOnly :theme="isDarkTheme ? 'dark' : 'light'" />
<MarkDownEditor :content="upgradeInfo.releaseNote" />
</div>
<template #footer>
<span class="dialog-footer">
@@ -81,10 +81,10 @@
</template>
<script setup lang="ts">
import DrawerHeader from '@/components/drawer-header/index.vue';
import MarkDownEditor from '@/components/mkdown-editor/index.vue';
import { getSettingInfo, loadReleaseNotes, loadUpgradeInfo, upgrade } from '@/api/modules/setting';
import MdEditor from 'md-editor-v3';
import i18n from '@/lang';
import 'md-editor-v3/lib/style.css';
import { MsgSuccess } from '@/utils/message';
import { copyText } from '@/utils/util';
import { onMounted, ref, computed } from 'vue';
@@ -93,7 +93,7 @@ import { ElMessageBox } from 'element-plus';
import { storeToRefs } from 'pinia';
const globalStore = GlobalStore();
const { isDarkTheme, docsUrl } = storeToRefs(globalStore);
const { docsUrl } = storeToRefs(globalStore);
const mobile = computed(() => {
return globalStore.isMobile();
+4
View File
@@ -212,6 +212,10 @@ export const DNSTypes = [
label: i18n.global.t('website.westCN'),
value: 'WestCN',
},
{
label: 'PorkBun',
value: 'PorkBun',
},
{
label: i18n.global.t('website.volcengine'),
value: 'Volcengine',
+2 -1
View File
@@ -333,6 +333,7 @@ const message = {
ssl: 'Certificate | Certificates',
database: 'Database | Databases',
ai_tools: 'AI',
mcp: 'MCP',
container: 'Container | Containers',
cronjob: 'Cron Job | Cron Jobs',
host: 'Host | Hosts',
@@ -833,7 +834,7 @@ const message = {
repo: 'Registries',
createRepo: 'Add',
createRepoHelper: 'Adding an HTTP-type repository requires restarting the Docker service.',
httpRepoHelper: 'Operating an HTTP-type repository requires restarting the Docker service.',
httpRepo: 'Choosing HTTP protocol requires restarting the Docker service to add it into insecure registries.',
delInsecure: 'Deletion of credit',
delInsecureHelper:
+2 -1
View File
@@ -333,6 +333,7 @@ const message = {
ssl: '証明書|証明書',
database: 'データベース|データベース',
ai_tools: 'AI',
mcp: 'MCP',
container: 'コンテナ|コンテナ',
cronjob: 'クロンジョブ|クロンの仕事',
host: 'ホスト|ホスト',
@@ -829,7 +830,7 @@ const message = {
repo: 'レジストリ',
createRepo: '追加',
createRepoHelper: 'HTTPタイプのリポジトリを追加するにはDockerサービスの再起動が必要です。',
httpRepoHelper: 'HTTPタイプのリポジトリを操作するにはDockerサービスの再起動が必要です。',
httpRepo:
'HTTPプロトコルを選択するには、Dockerサービスを再起動して不安定なレジストリに追加する必要があります。',
delInsecure: 'クレジットの削除',
+2 -1
View File
@@ -334,6 +334,7 @@ const message = {
ssl: '인증서 | 인증서들',
database: '데이터베이스 | 데이터베이스들',
ai_tools: 'AI',
mcp: 'MCP',
container: '컨테이너 | 컨테이너들',
cronjob: '크론 작업 | 크론 작업들',
host: '호스트 | 호스트들',
@@ -819,7 +820,7 @@ const message = {
repo: '레지스트리',
createRepo: '추가',
createRepoHelper: 'HTTP 타입 저장소 추가 시 Docker 서비스 재시작이 필요합니다.',
httpRepoHelper: 'HTTP 타입 저장소 작업 시 Docker 서비스 재시작이 필요합니다.',
httpRepo: 'HTTP 프로토콜을 선택하면 Docker 서비스를 재시작하여 불안정한 레지스트리에 추가해야 합니다.',
delInsecure: '신뢰할 수 없는 항목 삭제',
delInsecureHelper:
+2 -1
View File
@@ -340,6 +340,7 @@ const message = {
ssl: 'Certificate | Certificates',
database: 'Database | Databases',
ai_tools: 'AI',
mcp: 'MCP',
container: 'Container | Containers',
cronjob: 'Cron Job | Cron Jobs',
host: 'Host | Hosts',
@@ -845,7 +846,7 @@ const message = {
repo: 'Pendaftaran',
createRepo: 'Tambah',
createRepoHelper: 'Menambah repositori jenis HTTP memerlukan mulakan semula perkhidmatan Docker.',
httpRepoHelper: 'Mengoperasikan repositori jenis HTTP memerlukan mulakan semula perkhidmatan Docker.',
httpRepo:
'Memilih protokol HTTP memerlukan memulakan semula perkhidmatan Docker untuk menambahkannya ke pendaftaran tidak selamat.',
delInsecure: 'Padamkan pendaftaran tidak selamat',
+2 -1
View File
@@ -338,6 +338,7 @@ const message = {
ssl: 'Certificado | Certificados',
database: 'Banco de Dados | Bancos de Dados',
ai_tools: 'AI',
mcp: 'MCP',
container: 'Container | Containers',
cronjob: 'Tarefa Cron | Tarefas Cron',
host: 'Host | Hosts',
@@ -841,7 +842,7 @@ const message = {
repo: 'Registries',
createRepo: 'Adicionar',
createRepoHelper: 'Adicionar um repositório do tipo HTTP requer reinicialização do serviço Docker.',
httpRepoHelper: 'Operar um repositório do tipo HTTP requer reinicialização do serviço Docker.',
httpRepo:
'Escolher o protocolo HTTP requer reiniciar o serviço Docker para adicioná-lo a registries inseguros.',
delInsecure: 'Remover da lista de segurança',
+2 -1
View File
@@ -334,6 +334,7 @@ const message = {
ssl: 'Сертификат | Сертификаты',
database: 'База данных | Базы данных',
ai_tools: 'AI',
mcp: 'MCP',
container: 'Контейнер | Контейнеры',
cronjob: 'Cron | Задачи Cron',
host: 'Хост | Хосты',
@@ -843,7 +844,7 @@ const message = {
repo: 'Реестры',
createRepo: 'Добавить',
createRepoHelper: 'Добавление репозитория HTTP-типа требует перезапуска службы Docker.',
httpRepoHelper: 'Работа с репозиторием HTTP-типа требует перезапуска службы Docker.',
httpRepo: 'Выбор HTTP протокола требует перезапуска службы Docker для добавления в небезопасные реестры.',
delInsecure: 'Удаление учетных данных',
delInsecureHelper: 'Это перезапустит службу Docker для удаления из небезопасных реестров. Хотите продолжить?',
+2 -1
View File
@@ -327,6 +327,7 @@ const message = {
ssl: '證書',
database: '資料庫',
ai_tools: 'AI',
mcp: 'MCP',
container: '容器',
cronjob: '計劃任務',
host: '主機',
@@ -804,7 +805,7 @@ const message = {
repo: '倉庫',
createRepo: '新增倉庫',
createRepoHelper: '添加 HTTP 類型倉庫需要重啟 Docker 服務。',
httpRepoHelper: '操作 HTTP 類型倉庫需要重啟 Docker 服務。',
downloadUrl: '下載網址',
imageRepo: '鏡像倉庫',
repoHelper: '是否包含鏡像倉庫/組織/項目?',
+2 -1
View File
@@ -326,6 +326,7 @@ const message = {
ssl: '证书',
database: '数据库',
ai_tools: 'AI',
mcp: 'MCP',
container: '容器',
cronjob: '计划任务',
host: '主机',
@@ -806,7 +807,7 @@ const message = {
repo: '仓库',
createRepo: '添加仓库',
createRepoHelper: '添加 http 类型仓库需要重启 Docker 服务。',
httpRepoHelper: '操作 http 类型仓库需要重启 Docker 服务。',
downloadUrl: '下载地址',
imageRepo: '镜像仓库',
repoHelper: '是否包含镜像仓库/组织/项目?',
+1 -1
View File
@@ -24,7 +24,7 @@ const databaseRouter = {
name: 'MCPServer',
component: () => import('@/views/ai/mcp/server/index.vue'),
meta: {
title: 'MCP',
title: 'menu.mcp',
requiresAuth: true,
},
},
@@ -72,21 +72,18 @@
</div>
</div>
</div>
<MdEditor previewOnly v-model="app.readMe" :theme="isDarkTheme ? 'dark' : 'light'" />
<MarkDownEditor :content="app.readMe" />
</el-drawer>
<Install ref="installRef"></Install>
</template>
<script lang="ts" setup>
import MarkDownEditor from '@/components/mkdown-editor/index.vue';
import { GetApp, GetAppDetail } from '@/api/modules/app';
import MdEditor from 'md-editor-v3';
import { ref } from 'vue';
import Install from './install/index.vue';
import router from '@/routers';
import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
const globalStore = GlobalStore();
const { isDarkTheme } = storeToRefs(globalStore);
const app = ref<any>({});
const appDetail = ref<any>({});
+28 -4
View File
@@ -61,8 +61,9 @@
</template>
</LayoutContent>
<OpDialog ref="opRef" @search="search" />
<OpDialog ref="opRef" @search="search" @submit="submitDelete" />
<OperatorDialog @search="search" ref="dialogRef" />
<ConfirmDialog ref="confirmDialog" @confirm="submitDelete" />
</div>
</template>
@@ -87,6 +88,8 @@ const paginationConfig = reactive({
const searchName = ref();
const opRef = ref();
const confirmDialog = ref();
const currentRepo = ref();
const dockerStatus = ref('Running');
const loadStatus = async () => {
@@ -141,7 +144,16 @@ const onOpenDialog = async (
};
const onDelete = async (row: Container.RepoInfo) => {
let ids = [row.id];
currentRepo.value = row.id;
if (row.protocol === 'http') {
let params = {
header: i18n.global.t('container.repo'),
operationInfo: i18n.global.t('container.httpRepoHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmDialog.value!.acceptParams(params);
return;
}
opRef.value.acceptParams({
title: i18n.global.t('commons.button.delete'),
names: [row.name],
@@ -149,11 +161,23 @@ const onDelete = async (row: Container.RepoInfo) => {
i18n.global.t('container.repo'),
i18n.global.t('commons.button.delete'),
]),
api: deleteImageRepo,
params: { ids: ids },
api: null,
params: null,
});
};
const submitDelete = async () => {
loading.value = true;
await deleteImageRepo(currentRepo.value)
.then(() => {
loading.value = false;
search();
})
.catch(() => {
loading.value = false;
});
};
const onCheckConn = async (row: Container.RepoInfo) => {
loading.value = true;
await checkRepoStatus(row.id)
@@ -108,9 +108,13 @@ const dialogData = ref<DialogProps>({
title: '',
});
const confirmDialog = ref();
const oldUrl = ref();
const oldProto = ref();
const acceptParams = (params: DialogProps): void => {
dialogData.value = params;
oldUrl.value = params.rowData.downloadUrl;
oldProto.value = params.rowData.protocol;
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
drawerVisible.value = true;
};
@@ -147,13 +151,23 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (dialogData.value.rowData.protocol === 'https') {
let newProto = dialogData.value.rowData.protocol;
if (newProto === 'https' && dialogData.value.title === 'add') {
submit();
return;
}
if (newProto === oldProto.value) {
if (
(oldProto.value === 'http' && dialogData.value.rowData.downloadUrl === oldUrl.value) ||
oldProto.value === 'https'
) {
submit();
return;
}
}
let params = {
header: i18n.global.t('container.imageRepo'),
operationInfo: i18n.global.t('container.createRepoHelper'),
header: i18n.global.t('container.repo'),
operationInfo: i18n.global.t('container.httpRepoHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmDialog.value!.acceptParams(params);
+15 -5
View File
@@ -289,6 +289,7 @@ const chartOption = ref('network');
let timer: NodeJS.Timer | null = null;
let isInit = ref<boolean>(true);
let isActive = ref(true);
let isCurrentActive = ref(true);
const ioReadBytes = ref<Array<number>>([]);
const ioWriteBytes = ref<Array<number>>([]);
@@ -426,12 +427,20 @@ const onLoadBaseInfo = async (isInit: boolean, range: string) => {
currentInfo.value.uptime = resData.uptime;
loadAppCurrentInfo();
statusRef.value.acceptParams(currentInfo.value, baseInfo.value);
appRef.value.acceptParams();
statusRef.value?.acceptParams(currentInfo.value, baseInfo.value);
appRef.value?.acceptParams();
if (isInit) {
timer = setInterval(async () => {
if (isActive.value && !globalStore.isOnRestart) {
loadAppCurrentInfo();
try {
if (!isCurrentActive.value) {
throw new Error('jump out');
}
if (isActive.value && !globalStore.isOnRestart) {
await loadAppCurrentInfo();
}
} catch {
clearInterval(Number(timer));
timer = null;
}
}, 3000);
}
@@ -439,7 +448,7 @@ const onLoadBaseInfo = async (isInit: boolean, range: string) => {
const loadAppCurrentInfo = async () => {
await Promise.all([onLoadCurrentInfo('gpu'), onLoadCurrentInfo('basic'), onLoadCurrentInfo('ioNet')]);
statusRef.value.acceptParams(currentInfo.value, baseInfo.value);
statusRef.value?.acceptParams(currentInfo.value, baseInfo.value);
};
const onLoadCurrentInfo = async (scope: string) => {
@@ -661,6 +670,7 @@ onMounted(() => {
onBeforeUnmount(() => {
window.removeEventListener('focus', onFocus);
window.removeEventListener('blur', onBlur);
isCurrentActive.value = false;
clearInterval(Number(timer));
timer = null;
});
@@ -176,7 +176,7 @@
import { ref, reactive, onMounted, computed, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import type { ElForm } from 'element-plus';
import { loginApi, getCaptcha, mfaLoginApi, checkIsDemo, getLanguage, checkIsIntl } from '@/api/modules/auth';
import { loginApi, getCaptcha, mfaLoginApi, checkIsDemo, getAuthSetting, checkIsIntl } from '@/api/modules/auth';
import { GlobalStore, MenuStore, TabsStore } from '@/store';
import { MsgSuccess } from '@/utils/message';
import { useI18n } from 'vue-i18n';
@@ -318,7 +318,6 @@ const login = (formEl: FormInstance | undefined) => {
let requestLoginForm = {
name: loginForm.name,
password: encryptPassword(loginForm.password),
ignoreCaptcha: globalStore.ignoreCaptcha,
captcha: loginForm.captcha,
captchaID: captcha.captchaID,
authMethod: 'session',
@@ -400,11 +399,12 @@ const checkIsSystemDemo = async () => {
isDemo.value = res.data;
};
const loadLanguage = async () => {
const loadLoginSetting = async () => {
try {
const res = await getLanguage();
loginForm.language = res.data;
handleCommand(res.data);
const res = await getAuthSetting();
loginForm.language = res.data.language;
globalStore.ignoreCaptcha = !res.data.needCaptcha;
handleCommand(loginForm.language);
} catch (error) {}
};
@@ -426,7 +426,7 @@ onMounted(() => {
globalStore.isOnRestart = false;
checkIsSystemIntl();
loginVerify();
loadLanguage();
loadLoginSetting();
document.title = globalStore.themeConfig.panelName;
loginForm.agreeLicense = globalStore.agreeLicense;
checkIsSystemDemo();
@@ -138,6 +138,14 @@
<el-input v-model.trim="account.authorization['password']"></el-input>
</el-form-item>
</div>
<div v-if="account.type === 'PorkBun'">
<el-form-item label="API Key" prop="authorization.apiKey">
<el-input v-model.trim="account.authorization['apiKey']"></el-input>
</el-form-item>
<el-form-item label="Secret Key" prop="authorization.secretKey">
<el-input v-model.trim="account.authorization['secretKey']"></el-input>
</el-form-item>
</div>
</el-form>
</el-col>
</el-row>
+3 -3
View File
@@ -52,8 +52,6 @@ require (
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.3
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudapp v1.0.1115
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1115
github.com/tencentyun/cos-go-sdk-v5 v0.7.46
github.com/xlzd/gotp v0.1.0
go4.org v0.0.0-20230225012048-214862532bf5
@@ -64,7 +62,6 @@ require (
golang.org/x/sys v0.30.0
golang.org/x/term v0.29.0
golang.org/x/text v0.22.0
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.25.7
@@ -203,6 +200,7 @@ require (
github.com/nrdcg/freemyip v0.3.0 // indirect
github.com/nrdcg/mailinabox v0.2.0 // indirect
github.com/nrdcg/namesilo v0.2.1 // indirect
github.com/nrdcg/porkbun v0.4.0 // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
@@ -226,6 +224,7 @@ require (
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1115 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/theupdateframework/notary v0.7.0 // indirect
@@ -263,6 +262,7 @@ require (
golang.org/x/mod v0.22.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.28.0 // indirect
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/grpc v1.67.1 // indirect
+2 -2
View File
@@ -762,6 +762,8 @@ github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CF
github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc=
github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw=
github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -962,8 +964,6 @@ github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+z
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudapp v1.0.1115 h1:y0GrxBKOgY697oJbybR+WBrukZb/RB9bU06BtlPSWVA=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudapp v1.0.1115/go.mod h1:V1xvKmdqEnfD9cfA3z4v/XKxftR4Gdz7Jyp3Oi+UAtM=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1115 h1:lYeFC379r76seyzN7PHmSxv1ji9knmsXQJglDB/K0WE=