Compare commits

...
21 Commits
Author SHA1 Message Date
zhengkunwang 04edce94f9 feat: PHP 运行环境增加扩展源选择 (#2340) 2023-09-27 10:44:04 +08:00
ssongliu ccb81365f2 fix: 优化部分日志打印 (#2349) 2023-09-26 23:31:08 +08:00
ssongliu acf608cb7e fix: 解决部分容器创建失败的问题 (#2348) 2023-09-26 23:31:01 +08:00
zhengkunwang 2724708539 fix: 解决 Supervisor 停止之后一直同步状态的问题 (#2346) 2023-09-26 23:30:56 +08:00
zhengkunwang 50589a8d8d fix: 解决网站设置 PHP 源文之后没有立即生效的问题 (#2341) 2023-09-26 23:30:48 +08:00
ssongliu f06f487e8a fix: 修改容器操作提示信息 (#2324) 2023-09-16 10:02:16 +00:00
ssongliu 42dd668e1e fix: 解决恢复应用导致数据库备份文件丢失的问题 (#2323) 2023-09-16 09:00:16 +00:00
ssongliu 6cd00a072d fix: 优化版本排序 (#2322) 2023-09-16 08:30:16 +00:00
ssongliu 8faa3b6e51 fix: 修改数据库设置跳转路由 (#2320) 2023-09-16 06:36:15 +00:00
ssongliu 52198e9c0c fix: 数据库部分逻辑调整 (#2316) 2023-09-16 05:16:15 +00:00
ssongliu 173e3e9cc1 fix: 修改数据库备份逻辑 (#2315) 2023-09-15 10:48:14 +00:00
ssongliu 40c751cde2 fix: 修改数据库删除逻辑 (#2314) 2023-09-15 10:38:15 +00:00
zhengkunwang a3bf4513e1 fix: 修复日志文件下载不全的问题 (#2313) 2023-09-15 10:02:16 +00:00
zhengkunwang 4e7350aaae feat: 修改应用安装删除数据库逻辑 (#2311) 2023-09-15 08:46:13 +00:00
ssongliu 9ae935d06a fix: 解决数据库性能调整导致服务重启的问题 (#2308)
Refs #2301
2023-09-15 07:28:15 +00:00
ssongliu b6055eb058 fix: 解决数据冲突导致的升级失败问题 (#2306)
Refs #2290
2023-09-15 07:26:19 +00:00
wangdan-fit2cloud 0e158ffa2b fix: 修复导航样式冲突 (#2307)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-09-15 07:24:20 +00:00
zhengkunwang cc7260e121 style: 容器界面提示文字和操作保持一致 (#2305) 2023-09-15 07:22:18 +00:00
ssongliu 43b3ef47f3 fix: 解决数据库列表样式错乱的问题 (#2296) 2023-09-14 14:50:13 +00:00
zhengkunwang 21755223e1 fix: 解决限制安装的应用程序在首页还能继续安装的问题 (#2295) 2023-09-14 14:48:14 +00:00
ssongliu 08f0147d81 fix: 解决防火墙偶发的修改备注信息失败的问题 (#2292) 2023-09-14 14:44:14 +00:00
37 changed files with 354 additions and 278 deletions
-18
View File
@@ -153,24 +153,6 @@ type DBBaseInfo struct {
Port int64 `json:"port"`
}
type BackupDB struct {
MysqlName string `json:"mysqlName" validate:"required"`
DBName string `json:"dbName" validate:"required"`
}
type RecoverDB struct {
MysqlName string `json:"mysqlName" validate:"required"`
DBName string `json:"dbName" validate:"required"`
BackupName string `json:"backupName" validate:"required"`
}
type UploadRecover struct {
MysqlName string `json:"mysqlName" validate:"required"`
DBName string `json:"dbName" validate:"required"`
FileName string `json:"fileName"`
FileDir string `json:"fileDir"`
}
// redis
type ChangeRedisPass struct {
Value string `json:"value" validate:"required"`
+2
View File
@@ -17,6 +17,7 @@ type RuntimeCreate struct {
Image string `json:"image"`
Type string `json:"type"`
Version string `json:"version"`
Source string `json:"source"`
}
type RuntimeDelete struct {
@@ -30,4 +31,5 @@ type RuntimeUpdate struct {
Image string `json:"image"`
Version string `json:"version"`
Rebuild bool `json:"rebuild"`
Source string `json:"source"`
}
+1
View File
@@ -6,4 +6,5 @@ type RuntimeRes struct {
model.Runtime
AppParams []AppParam `json:"appParams"`
AppID uint `json:"appId"`
Source string `json:"source"`
}
+1
View File
@@ -42,6 +42,7 @@ type WebsiteLog struct {
Enable bool `json:"enable"`
Content string `json:"content"`
End bool `json:"end"`
Path string `json:"path"`
}
type PHPConfig struct {
+8 -7
View File
@@ -5,6 +5,14 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path"
"reflect"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
@@ -19,13 +27,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/files"
http2 "github.com/1Panel-dev/1Panel/backend/utils/http"
"gopkg.in/yaml.v3"
"io"
"net/http"
"os"
"path"
"reflect"
"strconv"
"strings"
)
type AppService struct {
+1 -1
View File
@@ -255,7 +255,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
func reCreateDB(dbID uint, app model.AppInstall, oldEnv string) (*model.DatabaseMysql, map[string]interface{}, error) {
mysqlService := NewIMysqlService()
ctx := context.Background()
_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, Database: app.Name, Type: app.App.Key, DeleteBackup: true, ForceDelete: true})
_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, Database: app.Name, Type: app.App.Key, DeleteBackup: false, ForceDelete: true})
envMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(oldEnv), &envMap); err != nil {
+6 -6
View File
@@ -150,7 +150,7 @@ func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
return err
}
databases, err := mysqlRepo.List(databaseRepo.WithByFrom(req.From))
databases, err := mysqlRepo.List(mysqlRepo.WithByMysqlName(req.Database))
if err != nil {
return err
}
@@ -235,11 +235,11 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
return err
}
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
}
if req.DeleteBackup {
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
}
localDir, err := loadLocalDir()
if err != nil && !req.ForceDelete {
return err
@@ -248,9 +248,9 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
if _, err := os.Stat(backupDir); err == nil {
_ = os.RemoveAll(backupDir)
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(req.Type), commonRepo.WithByName(req.Database), backupRepo.WithByDetailName(db.Name))
global.LOG.Infof("delete database %s-%s backups successful", req.Database, db.Name)
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(req.Type), commonRepo.WithByName(req.Database), backupRepo.WithByDetailName(db.Name))
_ = mysqlRepo.Delete(ctx, commonRepo.WithByID(db.ID))
return nil
+2
View File
@@ -267,6 +267,7 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool)
_ = u.addPortRecord(req)
}(req)
}
wg.Wait()
return nil
}
@@ -343,6 +344,7 @@ func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload boo
_ = u.addAddressRecord(req)
}(req)
}
wg.Wait()
if reload {
return client.Reload()
}
+5 -2
View File
@@ -94,7 +94,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
return
}
}
composeContent, envContent, forms, err := handleParams(create.Image, create.Type, newNameDir, create.Params)
composeContent, envContent, forms, err := handleParams(create.Image, create.Type, newNameDir, create.Source, create.Params)
if err != nil {
return
}
@@ -197,6 +197,9 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeRes, error) {
if err != nil {
return nil, err
}
if v, ok := envs["CONTAINER_PACKAGE_URL"]; ok {
res.Source = v
}
for _, form := range appForm.FormFields {
if v, ok := envs[form.EnvKey]; ok {
appParam := response.AppParam{
@@ -252,7 +255,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
return buserr.New(constant.ErrImageExist)
}
runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
composeContent, envContent, _, err := handleParams(req.Image, runtime.Type, runtimeDir, req.Params)
composeContent, envContent, _, err := handleParams(req.Image, runtime.Type, runtimeDir, req.Source, req.Params)
if err != nil {
return err
}
+2 -1
View File
@@ -89,7 +89,7 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
_ = runtimeRepo.Save(runtime)
}
func handleParams(image, runtimeType, runtimeDir string, params map[string]interface{}) (composeContent []byte, envContent []byte, forms []byte, err error) {
func handleParams(image, runtimeType, runtimeDir, source string, params map[string]interface{}) (composeContent []byte, envContent []byte, forms []byte, err error) {
fileOp := files.NewFileOp()
composeContent, err = fileOp.GetContent(path.Join(runtimeDir, "docker-compose.yml"))
if err != nil {
@@ -114,6 +114,7 @@ func handleParams(image, runtimeType, runtimeDir string, params map[string]inter
params["PHP_EXTENSIONS"] = strings.Join(strArray, ",")
}
}
params["CONTAINER_PACKAGE_URL"] = source
}
newMap := make(map[string]string)
handleMap(params, newMap)
+3 -2
View File
@@ -884,9 +884,10 @@ func updateRollbackStatus(id uint, status string, message string) {
}
func cpBinary(src []string, dst string) error {
stderr, err := cmd.Exec(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
global.LOG.Debugf(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
stdout, err := cmd.Exec(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
if err != nil {
return errors.New(stderr)
return fmt.Errorf("cp file failed, stdout: %v, err: %v", stdout, err)
}
return nil
}
+1 -1
View File
@@ -168,7 +168,7 @@ func snapCompress(snap snapHelper, rootDir string) {
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": err.Error()})
return
}
size := common.LoadSizeUnit(float64(stat.Size()))
size := common.LoadSizeUnit2F(float64(stat.Size()))
global.LOG.Debugf("compress successful! size of file: %s", size)
snap.Status.Compress = constant.StatusDone
snap.Status.Size = size
+4 -4
View File
@@ -132,25 +132,25 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
global.LOG.Info("backup original data successful, now start to upgrade!")
if err := cpBinary([]string{tmpDir + "/1panel"}, "/usr/local/bin/1panel"); err != nil {
u.handleRollback(fileOp, originalDir, 1)
global.LOG.Errorf("upgrade 1panel failed, err: %v", err)
u.handleRollback(fileOp, originalDir, 1)
return
}
if err := cpBinary([]string{tmpDir + "/1pctl"}, "/usr/local/bin/1pctl"); err != nil {
u.handleRollback(fileOp, originalDir, 2)
global.LOG.Errorf("upgrade 1pctl failed, err: %v", err)
u.handleRollback(fileOp, originalDir, 2)
return
}
if _, err := cmd.Execf("sed -i -e 's#BASE_DIR=.*#BASE_DIR=%s#g' /usr/local/bin/1pctl", global.CONF.System.BaseDir); err != nil {
u.handleRollback(fileOp, originalDir, 2)
global.LOG.Errorf("upgrade basedir in 1pctl failed, err: %v", err)
u.handleRollback(fileOp, originalDir, 2)
return
}
if err := cpBinary([]string{tmpDir + "/1panel.service"}, "/etc/systemd/system/1panel.service"); err != nil {
u.handleRollback(fileOp, originalDir, 3)
global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
u.handleRollback(fileOp, originalDir, 3)
return
}
+4
View File
@@ -941,6 +941,7 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
return nil, err
}
res.End = end
res.Path = filePath
res.Content = strings.Join(lines, "\n")
return res, nil
case constant.DisableLog:
@@ -1210,6 +1211,9 @@ func (w WebsiteService) UpdatePHPConfigFile(req request.WebsitePHPFileUpdate) er
if err := files.NewFileOp().WriteFile(configPath, strings.NewReader(req.Content), 0755); err != nil {
return err
}
if _, err := compose.Restart(runtimeInstall.GetComposePath()); err != nil {
return err
}
return nil
}
+1
View File
@@ -42,6 +42,7 @@ func Init() {
migrations.AddDatabases,
migrations.UpdateDatabase,
migrations.UpdateAppInstallResource,
migrations.DropDatabaseLocal,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+98 -116
View File
@@ -572,103 +572,40 @@ var AddDatabases = &gormigrate.Migration{
ID: "20230831-add-databases",
Migrate: func(tx *gorm.DB) error {
installRepo := repo.NewIAppInstallRepo()
mysqlInfo, err := installRepo.LoadBaseInfo("mysql", "")
if err == nil {
var mysqlDb model.Database
_ = tx.Where("name = ?", mysqlInfo.Name).First(&mysqlDb).Error
if mysqlDb.ID == 0 {
if err := tx.Create(&model.Database{
AppInstallID: mysqlInfo.ID,
Name: mysqlInfo.Name,
Type: "mysql",
Version: mysqlInfo.Version,
From: "local",
Address: mysqlInfo.ServiceName,
Port: service.DatabaseKeys["mysql"],
Username: "root",
Password: mysqlInfo.Password,
}).Error; err != nil {
return err
}
}
}
mariadbInfo, err := installRepo.LoadBaseInfo("mariadb", "")
if err == nil {
if err := tx.Create(&model.Database{
AppInstallID: mariadbInfo.ID,
Name: mariadbInfo.Name,
Type: "mariadb",
Version: mariadbInfo.Version,
From: "local",
Address: mariadbInfo.ServiceName,
Port: service.DatabaseKeys["mariadb"],
Username: "root",
Password: mariadbInfo.Password,
}).Error; err != nil {
_ = tx.Where("name = ? AND address = ?", "local", "127.0.0.1").Delete(&model.Database{}).Error
mysql := addDatabaseData(tx, installRepo, "mysql")
if mysql.AppInstallID != 0 {
if err := tx.Create(mysql).Error; err != nil {
return err
}
}
redisInfo, err := installRepo.LoadBaseInfo("redis", "")
if err == nil {
if err := tx.Create(&model.Database{
AppInstallID: redisInfo.ID,
Name: redisInfo.Name,
Type: "redis",
Version: redisInfo.Version,
From: "local",
Address: redisInfo.ServiceName,
Port: service.DatabaseKeys["redis"],
Username: "root",
Password: redisInfo.Password,
}).Error; err != nil {
mariadb := addDatabaseData(tx, installRepo, "mariadb")
if mariadb.AppInstallID != 0 {
if err := tx.Create(mariadb).Error; err != nil {
return err
}
}
pgInfo, err := installRepo.LoadBaseInfo("postgresql", "")
if err == nil {
if err := tx.Create(&model.Database{
AppInstallID: pgInfo.ID,
Name: pgInfo.Name,
Type: "postgresql",
Version: pgInfo.Version,
From: "local",
Address: pgInfo.ServiceName,
Port: service.DatabaseKeys["postgresql"],
Username: pgInfo.UserName,
Password: pgInfo.Password,
}).Error; err != nil {
redis := addDatabaseData(tx, installRepo, "redis")
if redis.AppInstallID != 0 {
if err := tx.Create(redis).Error; err != nil {
return err
}
}
mongodbInfo, err := installRepo.LoadBaseInfo("mongodb", "")
if err == nil {
if err := tx.Create(&model.Database{
AppInstallID: mongodbInfo.ID,
Name: mongodbInfo.Name,
Type: "mongodb",
Version: mongodbInfo.Version,
From: "local",
Address: mongodbInfo.ServiceName,
Port: service.DatabaseKeys["mongodb"],
Username: mongodbInfo.UserName,
Password: mongodbInfo.Password,
}).Error; err != nil {
postgresql := addDatabaseData(tx, installRepo, "postgresql")
if postgresql.AppInstallID != 0 {
if err := tx.Create(postgresql).Error; err != nil {
return err
}
}
memcachedInfo, err := installRepo.LoadBaseInfo("memcached", "")
if err == nil {
if err := tx.Create(&model.Database{
AppInstallID: memcachedInfo.ID,
Name: memcachedInfo.Name,
Type: "memcached",
Version: memcachedInfo.Version,
From: "local",
Address: memcachedInfo.ServiceName,
Port: service.DatabaseKeys["memcached"],
Username: "root",
Password: memcachedInfo.Password,
}).Error; err != nil {
mongodb := addDatabaseData(tx, installRepo, "mongodb")
if mongodb.AppInstallID != 0 {
if err := tx.Create(mongodb).Error; err != nil {
return err
}
}
memcached := addDatabaseData(tx, installRepo, "memcached")
if memcached.AppInstallID != 0 {
if err := tx.Create(memcached).Error; err != nil {
return err
}
}
@@ -691,37 +628,15 @@ var UpdateDatabase = &gormigrate.Migration{
return nil
}
for _, data := range datas {
if data.Name == "local" && data.Address == "127.0.0.1" && data.Type == "mysql" {
installRepo := repo.NewIAppInstallRepo()
mysqlInfo, err := installRepo.LoadBaseInfo("mysql", "")
if err != nil {
continue
}
pass, err := encrypt.StringEncrypt(data.Password)
if err != nil {
global.LOG.Errorf("encrypt database %s password failed, err: %v", data.Name, err)
continue
}
if err := tx.Model(&model.Database{}).Where("id = ?", data.ID).Updates(map[string]interface{}{
"app_install_id": mysqlInfo.ID,
"name": mysqlInfo.Name,
"password": pass,
"port": service.DatabaseKeys["mysql"],
"address": mysqlInfo.ServiceName,
}).Error; err != nil {
global.LOG.Errorf("updata database %s info failed, err: %v", data.Name, err)
}
} else {
pass, err := encrypt.StringEncrypt(data.Password)
if err != nil {
global.LOG.Errorf("encrypt database %s password failed, err: %v", data.Name, err)
continue
}
if err := tx.Model(&model.Database{}).Where("id = ?", data.ID).Updates(map[string]interface{}{
"password": pass,
}).Error; err != nil {
global.LOG.Errorf("updata database %s info failed, err: %v", data.Name, err)
}
pass, err := encrypt.StringEncrypt(data.Password)
if err != nil {
global.LOG.Errorf("encrypt database %s password failed, err: %v", data.Name, err)
continue
}
if err := tx.Model(&model.Database{}).Where("id = ?", data.ID).Updates(map[string]interface{}{
"password": pass,
}).Error; err != nil {
global.LOG.Errorf("updata database %s info failed, err: %v", data.Name, err)
}
}
@@ -759,3 +674,70 @@ var UpdateAppInstallResource = &gormigrate.Migration{
return nil
},
}
var DropDatabaseLocal = &gormigrate.Migration{
ID: "20230914-drop-database-local",
Migrate: func(tx *gorm.DB) error {
_ = tx.Where("name = ? AND address = ?", "local", "127.0.0.1").Delete(&model.Database{}).Error
return nil
},
}
func addDatabaseData(tx *gorm.DB, installRepo repo.IAppInstallRepo, appType string) *model.Database {
dbInfo, err := installRepo.LoadBaseInfo(appType, "")
if err != nil {
return &model.Database{}
}
if appType == "mysql" || appType == "redis" || appType == "mariadb" || appType == "memcached" {
dbInfo.UserName = "root"
}
database := &model.Database{
AppInstallID: dbInfo.ID,
Name: dbInfo.Name,
Type: appType,
Version: dbInfo.Version,
From: "local",
Address: dbInfo.ServiceName,
Port: service.DatabaseKeys[appType],
Username: dbInfo.UserName,
Password: dbInfo.Password,
}
var dbItem model.Database
_ = global.DB.Where("name = ?", dbInfo.Name).First(&dbItem).Error
if dbItem.ID != 0 {
if appType == "mysql" {
var (
backups []model.BackupRecord
mysqls []model.DatabaseMysql
)
_ = tx.Where("name = ? AND type = ?", dbItem.Name, "mysql").Find(&backups)
_ = tx.Where("`from` = ?", "local").Find(&mysqls)
for _, item := range backups {
isLocal := false
for _, mysql := range mysqls {
if item.Name == mysql.MysqlName && item.DetailName == mysql.Name {
isLocal = true
break
}
}
if !isLocal {
_ = tx.Model(&model.BackupRecord{}).Where("id = ?", item.ID).Updates(map[string]interface{}{
"name": "remote-" + dbItem.Name,
}).Error
}
}
}
if err := tx.Debug().Model(&model.DatabaseMysql{}).Where("mysql_name = ? AND `from` != ?", dbItem.Name, "local").Updates(map[string]interface{}{
"mysql_name": "remote-" + dbItem.Name,
}).Error; err != nil {
fmt.Println(err)
}
if err := tx.Debug().Model(&model.Database{}).Where("name = ?", dbItem.Name).Updates(map[string]interface{}{
"name": "remote-" + dbItem.Name,
}).Error; err != nil {
fmt.Println(err)
}
}
return database
}
+21
View File
@@ -24,6 +24,17 @@ func CompareVersion(version1 string, version2 string) bool {
version1s := strings.Split(version1, ".")
version2s := strings.Split(version2, ".")
if len(version2s) > len(version1s) {
for i := 0; i < len(version2s)-len(version1s); i++ {
version1s = append(version1s, "0")
}
}
if len(version1s) > len(version2s) {
for i := 0; i < len(version1s)-len(version2s); i++ {
version2s = append(version2s, "0")
}
}
n := min(len(version1s), len(version2s))
re := regexp.MustCompile("[0-9]+")
for i := 0; i < n; i++ {
@@ -147,6 +158,16 @@ func RemoveRepeatElement(a interface{}) (ret []interface{}) {
}
func LoadSizeUnit(value float64) string {
if value > 1048576 {
return fmt.Sprintf("%vM", value/1048576)
}
if value > 1024 {
return fmt.Sprintf("%vK", value/1024)
}
return fmt.Sprintf("%v", value)
}
func LoadSizeUnit2F(value float64) string {
if value > 1073741824 {
return fmt.Sprintf("%.2fG", value/1073741824)
}
+1 -23
View File
@@ -1,7 +1,6 @@
package client
import (
mathRand "math/rand"
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/common"
@@ -113,26 +112,5 @@ func randomPassword(user string) string {
if len(user) > 6 {
passwdItem = user[:6]
}
num := []rune("1234567890")
uppercase := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
lowercase := []rune("abcdefghijklmnopqrstuvwxyz")
special := []rune(".%@!~_-")
b := make([]rune, 10)
for i := 0; i < 2; i++ {
b[i] = lowercase[mathRand.Intn(len(lowercase))]
}
for i := 2; i < 4; i++ {
b[i] = uppercase[mathRand.Intn(len(uppercase))]
}
b[4] = special[mathRand.Intn(len(special))]
for i := 5; i < 9; i++ {
b[i] = num[mathRand.Intn(len(num))]
}
for i := len(b) - 1; i > 0; i-- {
j := mathRand.Intn(i + 1)
b[i], b[j] = b[j], b[i]
}
return passwdItem + "-" + (string(b))
return passwdItem + "@" + common.RandStrAndNum(8)
}
+3
View File
@@ -17688,6 +17688,9 @@ const docTemplate = `{
},
"end": {
"type": "boolean"
},
"path": {
"type": "string"
}
}
},
+3
View File
@@ -17681,6 +17681,9 @@
},
"end": {
"type": "boolean"
},
"path": {
"type": "string"
}
}
},
+2
View File
@@ -4016,6 +4016,8 @@ definitions:
type: boolean
end:
type: boolean
path:
type: string
type: object
response.WebsiteNginxConfig:
properties:
+2
View File
@@ -23,6 +23,7 @@ export namespace Runtime {
export interface RuntimeDTO extends Runtime {
appParams: App.InstallParams[];
appId: number;
source?: string;
}
export interface RuntimeCreate {
@@ -36,6 +37,7 @@ export namespace Runtime {
appId?: number;
version?: string;
rebuild?: boolean;
source?: string;
}
export interface RuntimeUpdate {
+1
View File
@@ -90,6 +90,7 @@ export namespace Website {
enable: boolean;
content: string;
end: boolean;
path: string;
}
export interface Domain {
+5 -1
View File
@@ -50,7 +50,7 @@
</div>
</el-card>
</div>
<div v-else>
<div v-if="!data.isExist && !isDB()">
<LayoutContent :title="getTitle(key)" :divider="true">
<template #main>
<div class="app-warn">
@@ -134,6 +134,10 @@ const goRouter = async (key: string) => {
router.push({ name: 'AppAll', query: { install: key } });
};
const isDB = () => {
return key.value === 'mysql' || key.value === 'mariadb';
};
const onCheck = async () => {
await CheckAppInstalled(key.value, name.value)
.then((res) => {
+9
View File
@@ -1658,6 +1658,15 @@ const message = {
phpPluginHelper:
'<a target="_blank" href="https://1panel.cn/docs/user_manual/websites/php/#php_1">View PHP extension list</a> ',
rebuild: 'Rebuild PHP App',
source: 'PHP extension source',
ustc: 'University of Science and Technology of China',
netease: 'Netease',
aliyun: 'Alibaba Cloud',
default: 'default',
tsinghua: 'Tsinghua University',
xtomhk: 'XTOM Mirror Station (Hong Kong)',
xtom: 'XTOM Mirror Station (Global)',
phpsourceHelper: 'Choose the appropriate source according to your network environment',
},
process: {
pid: 'Process ID',
+9
View File
@@ -1569,6 +1569,15 @@ const message = {
phpPluginHelper:
"<a target=“_blank” href='https://1panel.cn/docs/user_manual/websites/php/#php_1'>查看 PHP 擴展列表</a> ",
rebuild: '重建 PHP 應用',
source: 'PHP 擴展源',
ustc: '中國科學技術大學',
netease: '網易',
aliyun: '阿里雲',
default: '默認',
tsinghua: '清華大學',
xtomhk: 'XTOM 鏡像站(香港)',
xtom: 'XTOM 鏡像站(全球)',
phpsourceHelper: '根據你的網絡環境選擇合適的源',
},
process: {
pid: '進程ID',
+9
View File
@@ -1569,6 +1569,15 @@ const message = {
phpPluginHelper:
"<a target=“_blank” href='https://1panel.cn/docs/user_manual/websites/php/#php_1'>查看 PHP 扩展列表</a> ",
rebuild: '重建 PHP 应用',
source: 'PHP 扩展源',
ustc: '中国科学技术大学',
netease: '网易',
aliyun: '阿里云',
default: '默认',
tsinghua: '清华大学',
xtomhk: 'XTOM 镜像站(香港)',
xtom: 'XTOM 镜像站(全球)',
phpsourceHelper: '根据你的网络环境选择合适的源',
},
process: {
pid: '进程ID',
+3 -1
View File
@@ -180,11 +180,13 @@ html.dark {
.router_card_button {
.el-radio-button__inner {
background: none;
background: none !important;
}
.el-radio-button__original-radio:checked + .el-radio-button__inner {
color: #ffffff;
background-color: var(--panel-button-active) !important;
box-shadow: none !important;
border: none !important;
}
}
// content-box
@@ -1,5 +1,5 @@
<template>
<el-dialog v-model="open" :title="$t('app.delete')" width="30%" :close-on-click-modal="false">
<el-dialog v-model="open" :title="$t('container.' + operation)" width="30%" :close-on-click-modal="false">
<el-row>
<el-col :span="20" :offset="2">
<el-alert :title="msg" show-icon type="error" :closable="false"></el-alert>
@@ -64,7 +64,7 @@ const onConfirm = () => {
open.value = false;
loading.value = false;
em('search');
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.finally(() => {
open.value = false;
@@ -401,18 +401,20 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
dialogData.value.rowData!.labels = dialogData.value.rowData!.labelsStr.split('\n');
}
dialogData.value.rowData!.cmd = [];
if (dialogData.value.rowData?.cmdStr.indexOf(`'`) !== -1) {
let itemCmd = dialogData.value.rowData!.cmdStr.split(`'`);
for (const cmd of itemCmd) {
if (cmd && cmd !== ' ') {
if (dialogData.value.rowData?.cmdStr) {
if (dialogData.value.rowData?.cmdStr.indexOf(`'`) !== -1) {
let itemCmd = dialogData.value.rowData!.cmdStr.split(`'`);
for (const cmd of itemCmd) {
if (cmd && cmd !== ' ') {
dialogData.value.rowData!.cmd.push(cmd);
}
}
} else {
let itemCmd = dialogData.value.rowData!.cmdStr.split(` `);
for (const cmd of itemCmd) {
dialogData.value.rowData!.cmd.push(cmd);
}
}
} else {
let itemCmd = dialogData.value.rowData!.cmdStr.split(` `);
for (const cmd of itemCmd) {
dialogData.value.rowData!.cmd.push(cmd);
}
}
if (dialogData.value.rowData!.entrypointStr) {
dialogData.value.rowData!.entrypoint = dialogData.value.rowData!.entrypointStr.split(' ');
+1 -1
View File
@@ -164,7 +164,7 @@
</template>
</LayoutContent>
<div v-if="!currentDB?.id">
<div v-if="dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0">
<LayoutContent :title="'MySQL ' + $t('menu.database')" :divider="true">
<template #main>
<div class="app-warn">
+2 -1
View File
@@ -27,6 +27,7 @@
plain
round
size="small"
:disabled="app.limit == 1 && app.installed"
@click="goInstall(app.key)"
>
{{ $t('app.install') }}
@@ -57,7 +58,7 @@ let req = reactive({
});
let loading = ref(false);
let apps = ref<App.App[]>([]);
let apps = ref<App.AppDTO[]>([]);
const acceptParams = (): void => {
search(req);
@@ -228,7 +228,7 @@ const search = async () => {
needLoadStatus = true;
}
}
if (needLoadStatus) {
if (supervisorStatus.value.isRunning && needLoadStatus) {
setTimeout(loadStatus, 1000);
}
} catch (error) {}
@@ -252,7 +252,7 @@ const loadStatus = async () => {
}
}
}
if (needLoadStatus) {
if (supervisorStatus.value.isRunning && needLoadStatus) {
setTimeout(loadStatus, 2000);
}
} catch (error) {}
@@ -63,44 +63,60 @@
</el-col>
</el-row>
</el-form-item>
<el-form-item :label="$t('runtime.image')" prop="image">
<el-input v-model="runtime.image"></el-input>
</el-form-item>
<div v-if="initParam">
<el-form-item v-if="runtime.type === 'php'">
<el-alert :title="$t('runtime.buildHelper')" type="warning" :closable="false" />
</el-form-item>
<Params
v-if="mode === 'create'"
v-model:form="runtime.params"
v-model:params="appParams"
v-model:rules="rules"
></Params>
<EditParams
v-if="mode === 'edit'"
v-model:form="runtime.params"
v-model:params="editParams"
v-model:rules="rules"
></EditParams>
<el-form-item v-if="runtime.type === 'php'">
<el-alert type="info" :closable="false">
<span>{{ $t('runtime.extendHelper') }}</span>
<span v-html="$t('runtime.phpPluginHelper')"></span>
<br />
</el-alert>
</el-form-item>
<div v-if="runtime.type === 'php' && mode == 'edit'">
<div v-if="runtime.type === 'php'">
<el-form-item :label="$t('runtime.image')" prop="image">
<el-input v-model="runtime.image"></el-input>
</el-form-item>
<el-form-item :label="$t('runtime.source')" prop="source">
<el-select v-model="runtime.source" filterable allow-create default-first-option>
<el-option
v-for="(source, index) in phpSources"
:key="index"
:label="source.label + ' [' + source.value + ']'"
:value="source.value"
></el-option>
</el-select>
<span class="input-help">
{{ $t('runtime.phpsourceHelper') }}
</span>
</el-form-item>
<Params
v-if="mode === 'create'"
v-model:form="runtime.params"
v-model:params="appParams"
v-model:rules="rules"
></Params>
<EditParams
v-if="mode === 'edit'"
v-model:form="runtime.params"
v-model:params="editParams"
v-model:rules="rules"
></EditParams>
<el-form-item>
<el-checkbox v-model="runtime.rebuild">
{{ $t('runtime.rebuild') }}
</el-checkbox>
<el-alert :title="$t('runtime.buildHelper')" type="warning" :closable="false" />
</el-form-item>
<el-form-item>
<el-alert type="info" :closable="false">
<span>{{ $t('runtime.rebuildHelper') }}</span>
<span>{{ $t('runtime.extendHelper') }}</span>
<span v-html="$t('runtime.phpPluginHelper')"></span>
<br />
</el-alert>
</el-form-item>
<div v-if="mode == 'edit'">
<el-form-item>
<el-checkbox v-model="runtime.rebuild">
{{ $t('runtime.rebuild') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-alert type="info" :closable="false">
<span>{{ $t('runtime.rebuildHelper') }}</span>
<br />
</el-alert>
</el-form-item>
</div>
</div>
</div>
</div>
@@ -160,23 +176,59 @@ const appReq = reactive({
page: 1,
pageSize: 20,
});
const runtime = ref<Runtime.RuntimeCreate>({
const initData = (type: string) => ({
name: '',
appDetailId: undefined,
image: '',
params: {},
type: 'php',
type: type,
resource: 'appstore',
rebuild: false,
source: 'mirrors.ustc.edu.cn',
});
let runtime = reactive<Runtime.RuntimeCreate>(initData('php'));
const rules = ref<any>({
name: [Rules.appName],
resource: [Rules.requiredInput],
appId: [Rules.requiredSelect],
version: [Rules.requiredInput, Rules.paramCommon],
image: [Rules.requiredInput, Rules.imageName],
source: [Rules.requiredSelect],
});
const phpSources = [
{
label: i18n.global.t('runtime.ustc'),
value: 'mirrors.ustc.edu.cn',
},
{
label: i18n.global.t('runtime.netease'),
value: 'mirrors.163.com',
},
{
label: i18n.global.t('runtime.aliyun'),
value: 'mirrors.aliyun.com',
},
{
label: i18n.global.t('runtime.tsinghua'),
value: 'mirrors.tuna.tsinghua.edu.cn',
},
{
label: i18n.global.t('runtime.xtomhk'),
value: 'mirrors.xtom.com.hk',
},
{
label: i18n.global.t('runtime.xtom'),
value: 'mirrors.xtom.com',
},
{
label: i18n.global.t('runtime.default'),
value: 'dl-cdn.alpinelinux.org',
},
];
const em = defineEmits(['close']);
const handleClose = () => {
@@ -186,12 +238,12 @@ const handleClose = () => {
const changeResource = (resource: string) => {
if (resource === 'local') {
runtime.value.appDetailId = undefined;
runtime.value.version = '';
runtime.value.params = {};
runtime.value.image = '';
runtime.appDetailId = undefined;
runtime.version = '';
runtime.params = {};
runtime.image = '';
} else {
runtime.value.version = '';
runtime.version = '';
searchApp(null);
}
};
@@ -201,7 +253,7 @@ const searchApp = (appId: number) => {
apps.value = res.data.items || [];
if (res.data && res.data.items && res.data.items.length > 0) {
if (appId == null) {
runtime.value.appId = res.data.items[0].id;
runtime.appId = res.data.items[0].id;
getApp(res.data.items[0].key, mode.value);
} else {
res.data.items.forEach((item) => {
@@ -227,10 +279,10 @@ const changeApp = (appId: number) => {
const changeVersion = () => {
loading.value = true;
initParam.value = false;
GetAppDetail(runtime.value.appId, runtime.value.version, 'runtime')
GetAppDetail(runtime.appId, runtime.version, 'runtime')
.then((res) => {
runtime.value.appDetailId = res.data.id;
runtime.value.image = res.data.image + ':' + runtime.value.version;
runtime.appDetailId = res.data.id;
runtime.image = res.data.image + ':' + runtime.version;
appParams.value = res.data.params;
initParam.value = true;
})
@@ -243,7 +295,7 @@ const getApp = (appkey: string, mode: string) => {
GetApp(appkey).then((res) => {
appVersions.value = res.data.versions || [];
if (res.data.versions.length > 0) {
runtime.value.version = res.data.versions[0];
runtime.version = res.data.versions[0];
if (mode === 'create') {
changeVersion();
} else {
@@ -261,7 +313,7 @@ const submit = async (formEl: FormInstance | undefined) => {
}
if (mode.value == 'create') {
loading.value = true;
CreateRuntime(runtime.value)
CreateRuntime(runtime)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
handleClose();
@@ -271,7 +323,7 @@ const submit = async (formEl: FormInstance | undefined) => {
});
} else {
loading.value = true;
UpdateRuntime(runtime.value)
UpdateRuntime(runtime)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
handleClose();
@@ -287,7 +339,7 @@ const getRuntime = async (id: number) => {
try {
const res = await GetRuntime(id);
const data = res.data;
runtime.value = {
Object.assign(runtime, {
id: data.id,
name: data.name,
appDetailId: data.appDetailId,
@@ -298,7 +350,8 @@ const getRuntime = async (id: number) => {
appId: data.appId,
version: data.version,
rebuild: true,
};
source: data.source,
});
editParams.value = data.appParams;
if (mode.value == 'create') {
searchApp(data.appId);
@@ -312,16 +365,10 @@ const acceptParams = async (props: OperateRrops) => {
mode.value = props.mode;
initParam.value = false;
if (props.mode === 'create') {
runtime.value = {
name: '',
appDetailId: undefined,
image: '',
params: {},
type: props.type,
resource: 'appstore',
};
Object.assign(runtime, initData(props.type));
searchApp(null);
} else {
searchApp(null);
getRuntime(props.id);
}
open.value = true;
@@ -4,19 +4,14 @@
<el-form-item :label="$t('website.enable')">
<el-switch v-model="data.enable" @change="updateEnable"></el-switch>
</el-form-item>
<div style="margin-top: 10px">
<el-checkbox border v-model="tailLog" style="float: left" @change="changeTail">
<div class="mt-2.5">
<el-checkbox border v-model="tailLog" class="float-left" @change="changeTail">
{{ $t('commons.button.watch') }}
</el-checkbox>
<el-button
style="margin-left: 20px"
@click="onDownload"
icon="Download"
:disabled="data.content === ''"
>
<el-button class="ml-5" @click="onDownload" icon="Download" :disabled="data.content === ''">
{{ $t('file.download') }}
</el-button>
<el-button style="margin-left: 20px" @click="cleanLog" icon="Delete" :disabled="data.content === ''">
<el-button class="ml-5" @click="cleanLog" icon="Delete" :disabled="data.content === ''">
{{ $t('commons.button.clean') }}
</el-button>
</div>
@@ -46,8 +41,8 @@ import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue';
import { OpWebsiteLog } from '@/api/modules/website';
import { dateFormatForName, downloadWithContent } from '@/utils/util';
import { useDeleteData } from '@/hooks/use-delete-data';
import { downloadFile } from '@/utils/util';
const extensions = [javascript(), oneDark];
const props = defineProps({
@@ -70,6 +65,7 @@ const loading = ref(false);
const data = ref({
enable: false,
content: '',
path: '',
});
const tailLog = ref(false);
let timer: NodeJS.Timer | null = null;
@@ -165,7 +161,7 @@ const cleanLog = async () => {
};
const onDownload = async () => {
downloadWithContent(data.value.content, logType.value + '-' + dateFormatForName(new Date()) + '.log');
downloadFile(data.value.path);
};
const onCloseLog = async () => {
@@ -5,7 +5,7 @@
:placeholder="$t('commons.msg.noneData')"
:indent-with-tab="true"
:tabSize="4"
style="margin-top: 10px; max-height: 700px"
style="max-height: 700px"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
@@ -13,11 +13,9 @@
:extensions="extensions"
v-model="content"
/>
<div style="margin-top: 10px">
<el-button type="primary" @click="submit()">
{{ $t('nginx.saveAndReload') }}
</el-button>
</div>
<el-button type="primary" @click="submit()" class="mt-2.5">
{{ $t('nginx.saveAndReload') }}
</el-button>
</div>
</template>
<script lang="ts" setup>
@@ -5,7 +5,7 @@
:placeholder="$t('commons.msg.noneData')"
:indent-with-tab="true"
:tabSize="4"
style="margin-top: 10px; max-height: 700px"
style="max-height: 700px"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
@@ -13,11 +13,10 @@
:extensions="extensions"
v-model="content"
/>
<div style="margin-top: 10px">
<el-button type="primary" @click="submit()">
{{ $t('nginx.saveAndReload') }}
</el-button>
</div>
<el-button type="primary" @click="openUpdate()" class="mt-2.5">
{{ $t('nginx.saveAndReload') }}
</el-button>
<ConfirmDialog ref="confirmDialogRef" @confirm="submit()"></ConfirmDialog>
</div>
</template>
<script lang="ts" setup>
@@ -52,9 +51,10 @@ const id = computed(() => {
return props.id;
});
let data = ref<File.File>();
let loading = ref(false);
let content = ref('');
const data = ref<File.File>();
const loading = ref(false);
const content = ref('');
const confirmDialogRef = ref();
const get = () => {
loading.value = true;
@@ -68,6 +68,14 @@ const get = () => {
});
};
const openUpdate = async () => {
confirmDialogRef.value!.acceptParams({
header: i18n.global.t('database.confChange'),
operationInfo: i18n.global.t('database.restartNowHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
});
};
const submit = async () => {
loading.value = true;
UpdatePHPFile({