Compare commits
26 Commits
v1.1.0
...
pr@dev@ngi
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c618e6e03e | ||
![]() |
ad0c859b54 | ||
![]() |
d660b7d315 | ||
![]() |
2dbc7f28fd | ||
![]() |
e224bc4b24 | ||
![]() |
9c52977825 | ||
![]() |
cb151dc985 | ||
![]() |
24b9f8f705 | ||
![]() |
5592063e69 | ||
![]() |
7b19aab305 | ||
![]() |
5c50695bdc | ||
![]() |
1086597e3a | ||
![]() |
2944ea508e | ||
![]() |
5e887bd00c | ||
![]() |
76b3cf4d2b | ||
![]() |
4a9895218e | ||
![]() |
222593ea69 | ||
![]() |
05b7fd1f63 | ||
![]() |
256c04f3b8 | ||
![]() |
02577ef746 | ||
![]() |
059c3a0b80 | ||
![]() |
5ce1b1591a | ||
![]() |
6595ad6228 | ||
![]() |
e7608673d7 | ||
![]() |
4c8fc1defa | ||
![]() |
975663f0ff |
@@ -104,9 +104,9 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/backup/del [post]
|
||||
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"backup_accounts","output_colume":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":true,"db":"backup_accounts","output_colume":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
|
||||
func (b *BaseApi) DeleteBackup(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
var req dto.OperateByID
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
@@ -116,7 +116,7 @@ func (b *BaseApi) DeleteBackup(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := backupService.BatchDelete(req.Ids); err != nil {
|
||||
if err := backupService.Delete(req.ID); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
@@ -560,3 +560,91 @@ func (b *BaseApi) UpdatePHPFile(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get rewrite conf
|
||||
// @Description 获取伪静态配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxRewriteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/rewrite [post]
|
||||
func (b *BaseApi) GetRewriteConfig(c *gin.Context) {
|
||||
var req request.NginxRewriteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetRewriteConfig(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update rewrite conf
|
||||
// @Description 更新伪静态配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxRewriteUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/rewrite/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"websiteID","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"伪静态配置修改 [domain]","formatEN":"Nginx conf rewrite update [domain]"}
|
||||
func (b *BaseApi) UpdateRewriteConfig(c *gin.Context) {
|
||||
var req request.NginxRewriteUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateRewriteConfig(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update Site Dir
|
||||
// @Description 更新网站目录
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteUpdateDir true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dir/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录","formatEN":"Update domain [domain] dir"}
|
||||
func (b *BaseApi) UpdateSiteDir(c *gin.Context) {
|
||||
var req request.WebsiteUpdateDir
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateSiteDir(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update Site Dir permission
|
||||
// @Description 更新网站目录权限
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteUpdateDirPermission true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/dir/permission [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"websites","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新网站 [domain] 目录权限","formatEN":"Update domain [domain] dir permission"}
|
||||
func (b *BaseApi) UpdateSiteDirPermission(c *gin.Context) {
|
||||
var req request.WebsiteUpdateDirPermission
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateSitePermission(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
@@ -19,3 +19,14 @@ type NginxConfigUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Params interface{} `json:"params"`
|
||||
}
|
||||
|
||||
type NginxRewriteReq struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type NginxRewriteUpdate struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
@@ -145,3 +145,14 @@ type WebsitePHPFileUpdate struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteUpdateDir struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
SiteDir string `json:"siteDir" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteUpdateDirPermission struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
User string `json:"user" validate:"required"`
|
||||
Group string `json:"group" validate:"required"`
|
||||
}
|
||||
|
@@ -59,6 +59,7 @@ type AppInstalledDTO struct {
|
||||
type DatabaseConn struct {
|
||||
Password string `json:"password"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Port int64 `json:"port"`
|
||||
}
|
||||
|
||||
type AppService struct {
|
||||
|
@@ -47,3 +47,7 @@ type WebsiteLog struct {
|
||||
type PHPConfig struct {
|
||||
Params map[string]string `json:"params"`
|
||||
}
|
||||
|
||||
type NginxRewriteRes struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
@@ -4,25 +4,33 @@ import "time"
|
||||
|
||||
type Website struct {
|
||||
BaseModel
|
||||
Protocol string `gorm:"type:varchar(64);not null" json:"protocol"`
|
||||
PrimaryDomain string `gorm:"type:varchar(128);not null" json:"primaryDomain"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Alias string `gorm:"type:varchar(128);not null" json:"alias"`
|
||||
Remark string `gorm:"type:longtext;" json:"remark"`
|
||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||
HttpConfig string `gorm:"type:varchar(64);not null" json:"httpConfig"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
|
||||
WebsiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
|
||||
WebsiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
|
||||
Proxy string `gorm:"type:varchar(128);not null" json:"proxy"`
|
||||
ProxyType string `gorm:"type:varchar;" json:"proxyType"`
|
||||
ErrorLog bool `json:"errorLog"`
|
||||
AccessLog bool `json:"accessLog"`
|
||||
DefaultServer bool `json:"defaultServer"`
|
||||
RuntimeID uint `gorm:"type:integer" json:"runtimeID"`
|
||||
Domains []WebsiteDomain `json:"domains" gorm:"-:migration"`
|
||||
WebsiteSSL WebsiteSSL `json:"webSiteSSL" gorm:"-:migration"`
|
||||
Protocol string `gorm:"type:varchar;not null" json:"protocol"`
|
||||
PrimaryDomain string `gorm:"type:varchar;not null" json:"primaryDomain"`
|
||||
Type string `gorm:"type:varchar;not null" json:"type"`
|
||||
Alias string `gorm:"type:varchar;not null" json:"alias"`
|
||||
Remark string `gorm:"type:longtext;" json:"remark"`
|
||||
Status string `gorm:"type:varchar;not null" json:"status"`
|
||||
HttpConfig string `gorm:"type:varchar;not null" json:"httpConfig"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
|
||||
Proxy string `gorm:"type:varchar;" json:"proxy"`
|
||||
ProxyType string `gorm:"type:varchar;" json:"proxyType"`
|
||||
SiteDir string `gorm:"type:varchar;" json:"siteDir"`
|
||||
ErrorLog bool `json:"errorLog"`
|
||||
AccessLog bool `json:"accessLog"`
|
||||
DefaultServer bool `json:"defaultServer"`
|
||||
Rewrite string `gorm:"type:varchar" json:"rewrite"`
|
||||
|
||||
WebsiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
|
||||
WebsiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
|
||||
RuntimeID uint `gorm:"type:integer" json:"runtimeID"`
|
||||
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
|
||||
|
||||
User string `gorm:"type:varchar;" json:"user"`
|
||||
Group string `gorm:"type:varchar;" json:"group"`
|
||||
|
||||
Domains []WebsiteDomain `json:"domains" gorm:"-:migration"`
|
||||
WebsiteSSL WebsiteSSL `json:"webSiteSSL" gorm:"-:migration"`
|
||||
}
|
||||
|
||||
func (w Website) TableName() string {
|
||||
|
@@ -20,6 +20,7 @@ type ICronjobRepo interface {
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
|
||||
Create(cronjob *model.Cronjob) error
|
||||
WithByJobID(id int) DBOption
|
||||
WithByBackupID(id uint) DBOption
|
||||
WithByRecordDropID(id int) DBOption
|
||||
Save(id uint, cronjob model.Cronjob) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
@@ -114,6 +115,12 @@ func (c *CronjobRepo) WithByJobID(id int) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByBackupID(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("target_dir_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByRecordDropID(id int) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id < ?", id)
|
||||
|
@@ -141,6 +141,7 @@ func (a *AppInstallService) LoadConnInfo(key string) (response.DatabaseConn, err
|
||||
}
|
||||
data.Password = app.Password
|
||||
data.ServiceName = app.ServiceName
|
||||
data.Port = app.Port
|
||||
return data, nil
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
||||
@@ -26,7 +27,7 @@ type IBackupService interface {
|
||||
Create(backupDto dto.BackupOperate) error
|
||||
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
|
||||
Update(ireq dto.BackupOperate) error
|
||||
BatchDelete(ids []uint) error
|
||||
Delete(id uint) error
|
||||
BatchDeleteRecord(ids []uint) error
|
||||
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
|
||||
|
||||
@@ -159,8 +160,12 @@ func (u *BackupService) GetBuckets(backupDto dto.ForBuckets) ([]interface{}, err
|
||||
return client.ListBuckets()
|
||||
}
|
||||
|
||||
func (u *BackupService) BatchDelete(ids []uint) error {
|
||||
return backupRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||
func (u *BackupService) Delete(id uint) error {
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByBackupID(id))
|
||||
if len(cronjobs) != 0 {
|
||||
return buserr.New(constant.ErrBackupInUsed)
|
||||
}
|
||||
return backupRepo.Delete(commonRepo.WithByID(id))
|
||||
}
|
||||
|
||||
func (u *BackupService) BatchDeleteRecord(ids []uint) error {
|
||||
|
@@ -198,8 +198,10 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
|
||||
|
||||
ctx := context.Background()
|
||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
||||
return err
|
||||
if !checkImageExist(client, req.Image) {
|
||||
if err := pullImages(ctx, client, req.Image); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
container, err := client.ContainerCreate(ctx, config, hostConf, &network.NetworkingConfig{}, &v1.Platform{}, req.Name)
|
||||
if err != nil {
|
||||
@@ -332,6 +334,23 @@ func calculateNetwork(network map[string]types.NetworkStats) (float64, float64)
|
||||
return rx, tx
|
||||
}
|
||||
|
||||
func checkImageExist(client *client.Client, image string) bool {
|
||||
images, err := client.ImageList(context.Background(), types.ImageListOptions{})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
|
||||
for _, img := range images {
|
||||
for _, tag := range img.RepoTags {
|
||||
if tag == image || tag == image+":latest" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func pullImages(ctx context.Context, client *client.Client, image string) error {
|
||||
out, err := client.ImagePull(ctx, image, types.ImagePullOptions{})
|
||||
if err != nil {
|
||||
|
@@ -29,7 +29,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
if len(cronjob.Script) == 0 {
|
||||
return
|
||||
}
|
||||
stdout, errExec := cmd.Exec(cronjob.Script)
|
||||
stdout, errExec := cmd.ExecWithTimeOut(cronjob.Script, 5*time.Minute)
|
||||
if errExec != nil {
|
||||
err = errExec
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
if len(cronjob.URL) == 0 {
|
||||
return
|
||||
}
|
||||
stdout, errCurl := cmd.Exec("curl " + cronjob.URL)
|
||||
stdout, errCurl := cmd.ExecWithTimeOut("curl "+cronjob.URL, 5*time.Minute)
|
||||
if err != nil {
|
||||
err = errCurl
|
||||
}
|
||||
@@ -170,7 +170,7 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
|
||||
commands := fmt.Sprintf("tar zcvf %s %s %s", targetDir+"/"+name, excludeRules, path)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.Exec(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 5*time.Minute)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
@@ -187,7 +187,7 @@ func handleUnTar(sourceFile, targetDir string) error {
|
||||
|
||||
commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.Exec(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 5*time.Minute)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
|
@@ -36,10 +36,14 @@ func NewIFirewallService() IFirewallService {
|
||||
|
||||
func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
|
||||
var baseInfo dto.FirewallBaseInfo
|
||||
baseInfo.PingStatus = u.pingStatus()
|
||||
baseInfo.Status = "not running"
|
||||
baseInfo.Version = "-"
|
||||
baseInfo.Name = "-"
|
||||
client, err := firewall.NewFirewallClient()
|
||||
if err != nil {
|
||||
if err.Error() == "no such type" {
|
||||
return dto.FirewallBaseInfo{Name: "-", Version: "-", Status: "not running", PingStatus: "Disable"}, nil
|
||||
return baseInfo, nil
|
||||
}
|
||||
return baseInfo, err
|
||||
}
|
||||
@@ -48,12 +52,7 @@ func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
|
||||
if err != nil {
|
||||
return baseInfo, err
|
||||
}
|
||||
baseInfo.PingStatus, err = u.PingStatus()
|
||||
if err != nil {
|
||||
return baseInfo, err
|
||||
}
|
||||
if baseInfo.Status == "not running" {
|
||||
baseInfo.Version = "-"
|
||||
return baseInfo, err
|
||||
}
|
||||
baseInfo.Version, err = client.Version()
|
||||
@@ -142,14 +141,10 @@ func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
if err := client.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
serverPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort"))
|
||||
if err != nil {
|
||||
if err := u.addPortsBeforeStart(client); err != nil {
|
||||
_ = client.Stop()
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: serverPort.Value, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = client.Reload()
|
||||
_, _ = cmd.Exec("systemctl restart docker")
|
||||
return nil
|
||||
case "stop":
|
||||
@@ -159,9 +154,9 @@ func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
_, _ = cmd.Exec("systemctl restart docker")
|
||||
return nil
|
||||
case "disablePing":
|
||||
return u.UpdatePingStatus("0")
|
||||
return u.updatePingStatus("0")
|
||||
case "enablePing":
|
||||
return u.UpdatePingStatus("1")
|
||||
return u.updatePingStatus("1")
|
||||
}
|
||||
return fmt.Errorf("not support such operation: %s", operation)
|
||||
}
|
||||
@@ -369,15 +364,18 @@ func (u *FirewallService) loadPortByApp() []portOfApp {
|
||||
return datas
|
||||
}
|
||||
|
||||
func (u *FirewallService) PingStatus() (string, error) {
|
||||
func (u *FirewallService) pingStatus() string {
|
||||
if _, err := os.Stat("/etc/sysctl.conf"); err != nil {
|
||||
return constant.StatusNone
|
||||
}
|
||||
stdout, _ := cmd.Exec("sudo cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= ")
|
||||
if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" {
|
||||
return constant.StatusEnable, nil
|
||||
return constant.StatusEnable
|
||||
}
|
||||
return constant.StatusDisable, nil
|
||||
return constant.StatusDisable
|
||||
}
|
||||
|
||||
func (u *FirewallService) UpdatePingStatus(enabel string) error {
|
||||
func (u *FirewallService) updatePingStatus(enabel string) error {
|
||||
lineBytes, err := os.ReadFile(confPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -413,3 +411,30 @@ func (u *FirewallService) UpdatePingStatus(enabel string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) error {
|
||||
serverPort, err := settingRepo.Get(settingRepo.WithByKey("ServerPort"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: serverPort.Value, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: "22", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: "80", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Port(fireClient.FireInfo{Port: "443", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
apps := u.loadPortByApp()
|
||||
for _, app := range apps {
|
||||
if err := client.Port(fireClient.FireInfo{Port: app.HttpPort, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return client.Reload()
|
||||
}
|
||||
|
@@ -8,7 +8,9 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/subosito/gotenv"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
@@ -87,7 +89,16 @@ func getComposeService(name, runtimeDir string, composeFile, env []byte, skipNor
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
composeService, err := docker.NewComposeService()
|
||||
logPath := path.Join(runtimeDir, "build.log")
|
||||
fileOp := files.NewFileOp()
|
||||
if fileOp.Stat(logPath) {
|
||||
_ = fileOp.DeleteFile(logPath)
|
||||
}
|
||||
file, err := os.Create(logPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
composeService, err := docker.NewComposeService(command.WithOutputStream(file))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -854,7 +854,7 @@ func (u *SnapshotService) handleTar(sourceDir, targetDir, name, exclusionRules s
|
||||
|
||||
commands := fmt.Sprintf("tar --warning=no-file-changed -zcf %s %s -C %s .", targetDir+"/"+name, exStr, sourceDir)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.Exec(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
@@ -871,7 +871,7 @@ func (u *SnapshotService) handleUnTar(sourceDir, targetDir string) error {
|
||||
|
||||
commands := fmt.Sprintf("tar -zxf %s -C %s .", sourceDir, targetDir)
|
||||
global.LOG.Debug(commands)
|
||||
stdout, err := cmd.Exec(commands)
|
||||
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
|
||||
return errors.New(stdout)
|
||||
|
@@ -148,7 +148,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
go writeLogs(req.Version)
|
||||
_ = settingRepo.Update("SystemVersion", req.Version)
|
||||
_ = settingRepo.Update("SystemStatus", "Free")
|
||||
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
|
||||
_, _ = cmd.ExecWithTimeOut("systemctl daemon-reload && systemctl restart 1panel.service", 1*time.Minute)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
@@ -2,12 +2,15 @@ package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"path"
|
||||
@@ -57,6 +60,10 @@ type IWebsiteService interface {
|
||||
GetPHPConfig(id uint) (*response.PHPConfig, error)
|
||||
UpdatePHPConfig(req request.WebsitePHPConfigUpdate) error
|
||||
UpdatePHPConfigFile(req request.WebsitePHPFileUpdate) error
|
||||
GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error)
|
||||
UpdateRewriteConfig(req request.NginxRewriteUpdate) error
|
||||
UpdateSiteDir(req request.WebsiteUpdateDir) error
|
||||
UpdateSitePermission(req request.WebsiteUpdateDirPermission) error
|
||||
}
|
||||
|
||||
func NewIWebsiteService() IWebsiteService {
|
||||
@@ -143,6 +150,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
WebsiteGroupID: create.WebsiteGroupID,
|
||||
Protocol: constant.ProtocolHTTP,
|
||||
Proxy: create.Proxy,
|
||||
SiteDir: "/",
|
||||
AccessLog: true,
|
||||
ErrorLog: true,
|
||||
}
|
||||
@@ -981,3 +989,113 @@ func (w WebsiteService) UpdatePHPConfigFile(req request.WebsitePHPFileUpdate) er
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateRewriteConfig(req request.NginxRewriteUpdate) error {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxFull, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
includePath := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.PrimaryDomain)
|
||||
absolutePath := path.Join(nginxFull.Install.GetPath(), includePath)
|
||||
fileOp := files.NewFileOp()
|
||||
var oldRewriteContent []byte
|
||||
if !fileOp.Stat(path.Dir(absolutePath)) {
|
||||
if err := fileOp.CreateDir(path.Dir(absolutePath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !fileOp.Stat(absolutePath) {
|
||||
if err := fileOp.CreateFile(absolutePath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
oldRewriteContent, err = fileOp.GetContent(absolutePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{includePath}}}, &website); err != nil {
|
||||
_ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), 0755)
|
||||
return err
|
||||
}
|
||||
website.Rewrite = req.Name
|
||||
return websiteRepo.Save(context.Background(), &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error) {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var contentByte []byte
|
||||
if req.Name == "current" {
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rewriteConfPath := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "rewrite", fmt.Sprintf("%s.conf", website.PrimaryDomain))
|
||||
fileOp := files.NewFileOp()
|
||||
if fileOp.Stat(rewriteConfPath) {
|
||||
contentByte, err = fileOp.GetContent(rewriteConfPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rewriteFile := fmt.Sprintf("rewrite/%s.conf", strings.ToLower(req.Name))
|
||||
contentByte, err = nginx_conf.Rewrites.ReadFile(rewriteFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &response.NginxRewriteRes{
|
||||
Content: string(contentByte),
|
||||
}, err
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateSiteDir(req request.WebsiteUpdateDir) error {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runDir := req.SiteDir
|
||||
siteDir := path.Join("/www/sites", website.Alias, "index")
|
||||
if req.SiteDir != "/" {
|
||||
siteDir = fmt.Sprintf("%s/%s", siteDir, req.SiteDir)
|
||||
}
|
||||
if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "root", Params: []string{siteDir}}}, &website); err != nil {
|
||||
return err
|
||||
}
|
||||
website.SiteDir = runDir
|
||||
return websiteRepo.Save(context.Background(), &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateSitePermission(req request.WebsiteUpdateDirPermission) error {
|
||||
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
absoluteIndexPath := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "index")
|
||||
if website.SiteDir != "/" {
|
||||
absoluteIndexPath = path.Join(absoluteIndexPath, website.SiteDir)
|
||||
}
|
||||
chownCmd := fmt.Sprintf("chown -R %s:%s %s", req.User, req.Group, absoluteIndexPath)
|
||||
if _, err := cmd.ExecWithTimeOut(chownCmd, 1*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
website.User = req.User
|
||||
website.Group = req.Group
|
||||
return websiteRepo.Save(context.Background(), &website)
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -58,8 +59,6 @@ func createIndexFile(website *model.Website, runtime *model.Runtime) error {
|
||||
if runtime.Type == constant.RuntimePHP {
|
||||
indexPath = path.Join(indexFolder, "index.php")
|
||||
indexContent = string(nginx_conf.IndexPHP)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +73,11 @@ func createIndexFile(website *model.Website, runtime *model.Runtime) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if website.Type == constant.Runtime && runtime.Resource == constant.ResourceAppstore {
|
||||
if err := chownRootDir(indexFolder); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := fileOp.WriteFile(indexPath, strings.NewReader(indexContent), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -97,19 +101,21 @@ func createWebsiteFolder(nginxInstall model.AppInstall, website *model.Website,
|
||||
if err := fileOp.CreateFile(path.Join(siteFolder, "log", "error.log")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.CreateDir(path.Join(siteFolder, "index"), 0755); err != nil {
|
||||
if err := fileOp.CreateDir(path.Join(siteFolder, "index"), 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.CreateDir(path.Join(siteFolder, "ssl"), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if website.Type == constant.Runtime && runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceLocal {
|
||||
phpPoolDir := path.Join(siteFolder, "php-pool")
|
||||
if err := fileOp.CreateDir(phpPoolDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.CreateFile(path.Join(phpPoolDir, "php-fpm.sock")); err != nil {
|
||||
return err
|
||||
if website.Type == constant.Runtime {
|
||||
if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceLocal {
|
||||
phpPoolDir := path.Join(siteFolder, "php-pool")
|
||||
if err := fileOp.CreateDir(phpPoolDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.CreateFile(path.Join(phpPoolDir, "php-fpm.sock")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if website.Type == constant.Static || website.Type == constant.Runtime {
|
||||
@@ -505,3 +511,11 @@ func checkIsLinkApp(website model.Website) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func chownRootDir(path string) error {
|
||||
_, err := cmd.ExecWithTimeOut(fmt.Sprintf("chown -R 1000:1000 %s", path), 1*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -114,3 +114,7 @@ var (
|
||||
ErrImageExist = "ErrImageExist"
|
||||
ErrDelWithWebsite = "ErrDelWithWebsite"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBackupInUsed = "ErrBackupInUsed"
|
||||
)
|
||||
|
@@ -9,4 +9,5 @@ const (
|
||||
StatusUploading = "Uploading"
|
||||
StatusEnable = "Enable"
|
||||
StatusDisable = "Disable"
|
||||
StatusNone = "None"
|
||||
)
|
||||
|
@@ -67,4 +67,7 @@ ErrDirNotFound: "The build folder does not exist! Please check file integrity!
|
||||
ErrFileNotExist: "{{ .detail }} file does not exist! Please check source file integrity!"
|
||||
ErrImageBuildErr: "Image build failed"
|
||||
ErrImageExist: "Image is already exist!"
|
||||
ErrDelWithWebsite: "The operating environment has been associated with a website and cannot be deleted"
|
||||
ErrDelWithWebsite: "The operating environment has been associated with a website and cannot be deleted"
|
||||
|
||||
#setting
|
||||
ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted."
|
@@ -67,4 +67,7 @@ ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
||||
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
|
||||
ErrImageBuildErr: "镜像 build 失败"
|
||||
ErrImageExist: "镜像已存在!"
|
||||
ErrDelWithWebsite: "运行环境已经关联网站,无法删除"
|
||||
ErrDelWithWebsite: "运行环境已经关联网站,无法删除"
|
||||
|
||||
#setting
|
||||
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
|
||||
|
@@ -276,8 +276,14 @@ var UpdateTableHost = &gormigrate.Migration{
|
||||
}
|
||||
|
||||
var UpdateTableWebsite = &gormigrate.Migration{
|
||||
ID: "20230406-update-table-website",
|
||||
ID: "20230418-update-table-website",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(&model.Website{})
|
||||
if err := tx.AutoMigrate(&model.Website{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&model.Website{}).Where("1 = 1").Update("site_dir", "/").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
@@ -1,10 +1,11 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-contrib/gzip"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/gzip"
|
||||
|
||||
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||
@@ -40,9 +41,6 @@ func setWebStatic(rootRouter *gin.Engine) {
|
||||
func Routers() *gin.Engine {
|
||||
Router := gin.Default()
|
||||
|
||||
docs.SwaggerInfo.BasePath = "/api/v1"
|
||||
Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
||||
|
||||
Router.Use(middleware.OperationLog())
|
||||
// Router.Use(middleware.CSRF())
|
||||
// Router.Use(middleware.LoadCsrfToken())
|
||||
|
@@ -45,5 +45,11 @@ func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) {
|
||||
groupRouter.GET("/php/config/:id", baseApi.GetWebsitePHPConfig)
|
||||
groupRouter.POST("/php/config", baseApi.UpdateWebsitePHPConfig)
|
||||
groupRouter.POST("/php/update", baseApi.UpdatePHPFile)
|
||||
|
||||
groupRouter.POST("/rewrite", baseApi.GetRewriteConfig)
|
||||
groupRouter.POST("/rewrite/update", baseApi.UpdateRewriteConfig)
|
||||
|
||||
groupRouter.POST("/dir/update", baseApi.UpdateSiteDir)
|
||||
groupRouter.POST("/dir/permission", baseApi.UpdateSiteDirPermission)
|
||||
}
|
||||
}
|
||||
|
@@ -45,6 +45,7 @@ var repeatKeys = map[string]struct {
|
||||
"if": {},
|
||||
"proxy_set_header": {},
|
||||
"location": {},
|
||||
"include": {},
|
||||
}
|
||||
|
||||
func IsRepeatKey(key string) bool {
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
package nginx_conf
|
||||
|
||||
import (
|
||||
"embed"
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
@@ -15,3 +16,6 @@ var Index []byte
|
||||
|
||||
//go:embed index.php
|
||||
var IndexPHP []byte
|
||||
|
||||
//go:embed rewrite/*
|
||||
var Rewrites embed.FS
|
||||
|
6
cmd/server/nginx_conf/rewrite/crmeb.conf
Normal file
6
cmd/server/nginx_conf/rewrite/crmeb.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^(.*)$ /index.php?s=/$1 last;
|
||||
break;
|
||||
}
|
||||
}
|
5
cmd/server/nginx_conf/rewrite/dabr.conf
Normal file
5
cmd/server/nginx_conf/rewrite/dabr.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^/(.*)$ /index.php?q=$1 last;
|
||||
}
|
||||
}
|
7
cmd/server/nginx_conf/rewrite/dbshop.conf
Normal file
7
cmd/server/nginx_conf/rewrite/dbshop.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
location /{
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ~ \.htaccess{
|
||||
deny all;
|
||||
}
|
10
cmd/server/nginx_conf/rewrite/dedecms.conf
Normal file
10
cmd/server/nginx_conf/rewrite/dedecms.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
rewrite "^/list-([0-9]+)\.html$" /plus/list.php?tid=$1 last;
|
||||
rewrite "^/list-([0-9]+)-([0-9]+)-([0-9]+)\.html$" /plus/list.php?tid=$1&totalresult=$2&PageNo=$3 last;
|
||||
rewrite "^/view-([0-9]+)-1\.html$" /plus/view.php?arcID=$1 last;
|
||||
rewrite "^/view-([0-9]+)-([0-9]+)\.html$" /plus/view.php?aid=$1&pageno=$2 last;
|
||||
rewrite "^/plus/list-([0-9]+)\.html$" /plus/list.php?tid=$1 last;
|
||||
rewrite "^/plus/list-([0-9]+)-([0-9]+)-([0-9]+)\.html$" /plus/list.php?tid=$1&totalresult=$2&PageNo=$3 last;
|
||||
rewrite "^/plus/view-([0-9]+)-1\.html$" /plus/view.php?arcID=$1 last;
|
||||
rewrite "^/plus/view-([0-9]+)-([0-9]+)\.html$" /plus/view.php?aid=$1&pageno=$2 last;
|
||||
rewrite "^/tags.html$" /tags.php last;
|
||||
rewrite "^/tag-([0-9]+)-([0-9]+)\.html$" /tags.php?/$1/$2/ last;
|
0
cmd/server/nginx_conf/rewrite/default.conf
Normal file
0
cmd/server/nginx_conf/rewrite/default.conf
Normal file
7
cmd/server/nginx_conf/rewrite/discuz.conf
Normal file
7
cmd/server/nginx_conf/rewrite/discuz.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
location / {
|
||||
rewrite ^/archiver/((fid|tid)-[\w\-]+\.html)$ /archiver/index.php?$1 last;
|
||||
rewrite ^/forum-([0-9]+)-([0-9]+)\.html$ /forumdisplay.php?fid=$1&page=$2 last;
|
||||
rewrite ^/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /viewthread.php?tid=$1&extra=page%3D$3&page=$2 last;
|
||||
rewrite ^/space-(username|uid)-(.+)\.html$ /space.php?$1=$2 last;
|
||||
rewrite ^/tag-(.+)\.html$ /tag.php?name=$1 last;
|
||||
}
|
12
cmd/server/nginx_conf/rewrite/discuzx.conf
Normal file
12
cmd/server/nginx_conf/rewrite/discuzx.conf
Normal file
@@ -0,0 +1,12 @@
|
||||
rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last;
|
||||
rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;
|
||||
rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last;
|
||||
rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last;
|
||||
rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last;
|
||||
rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last;
|
||||
if (!-e $request_filename) {
|
||||
return 404;
|
||||
}
|
14
cmd/server/nginx_conf/rewrite/discuzx2.conf
Normal file
14
cmd/server/nginx_conf/rewrite/discuzx2.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
location /bbs/ {
|
||||
rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last;
|
||||
rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;
|
||||
rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last;
|
||||
rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last;
|
||||
rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last;
|
||||
rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last;
|
||||
if (!-e $request_filename) {
|
||||
return 404;
|
||||
}
|
||||
}
|
15
cmd/server/nginx_conf/rewrite/discuzx3.conf
Normal file
15
cmd/server/nginx_conf/rewrite/discuzx3.conf
Normal file
@@ -0,0 +1,15 @@
|
||||
location / {
|
||||
rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last;
|
||||
rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;
|
||||
rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last;
|
||||
rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last;
|
||||
rewrite ^([^\.]*)/(fid|tid)-([0-9]+)\.html$ $1/index.php?action=$2&value=$3 last;
|
||||
rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last;
|
||||
if (!-e $request_filename) {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
|
3
cmd/server/nginx_conf/rewrite/drupal.conf
Normal file
3
cmd/server/nginx_conf/rewrite/drupal.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^/(.*)$ /index.php?q=$1 last;
|
||||
}
|
32
cmd/server/nginx_conf/rewrite/ecshop.conf
Normal file
32
cmd/server/nginx_conf/rewrite/ecshop.conf
Normal file
@@ -0,0 +1,32 @@
|
||||
if (!-e $request_filename)
|
||||
{
|
||||
rewrite "^/index\.html" /index.php last;
|
||||
rewrite "^/category$" /index.php last;
|
||||
rewrite "^/feed-c([0-9]+)\.xml$" /feed.php?cat=$1 last;
|
||||
rewrite "^/feed-b([0-9]+)\.xml$" /feed.php?brand=$1 last;
|
||||
rewrite "^/feed\.xml$" /feed.php last;
|
||||
rewrite "^/category-([0-9]+)-b([0-9]+)-min([0-9]+)-max([0-9]+)-attr([^-]*)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /category.php?id=$1&brand=$2&price_min=$3&price_max=$4&filter_attr=$5&page=$6&sort=$7&order=$8 last;
|
||||
rewrite "^/category-([0-9]+)-b([0-9]+)-min([0-9]+)-max([0-9]+)-attr([^-]*)(.*)\.html$" /category.php?id=$1&brand=$2&price_min=$3&price_max=$4&filter_attr=$5 last;
|
||||
rewrite "^/category-([0-9]+)-b([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /category.php?id=$1&brand=$2&page=$3&sort=$4&order=$5 last;
|
||||
rewrite "^/category-([0-9]+)-b([0-9]+)-([0-9]+)(.*)\.html$" /category.php?id=$1&brand=$2&page=$3 last;
|
||||
rewrite "^/category-([0-9]+)-b([0-9]+)(.*)\.html$" /category.php?id=$1&brand=$2 last;
|
||||
rewrite "^/category-([0-9]+)(.*)\.html$" /category.php?id=$1 last;
|
||||
rewrite "^/goods-([0-9]+)(.*)\.html" /goods.php?id=$1 last;
|
||||
rewrite "^/article_cat-([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /article_cat.php?id=$1&page=$2&sort=$3&order=$4 last;
|
||||
rewrite "^/article_cat-([0-9]+)-([0-9]+)(.*)\.html$" /article_cat.php?id=$1&page=$2 last;
|
||||
rewrite "^/article_cat-([0-9]+)(.*)\.html$" /article_cat.php?id=$1 last;
|
||||
rewrite "^/article-([0-9]+)(.*)\.html$" /article.php?id=$1 last;
|
||||
rewrite "^/brand-([0-9]+)-c([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)\.html" /brand.php?id=$1&cat=$2&page=$3&sort=$4&order=$5 last;
|
||||
rewrite "^/brand-([0-9]+)-c([0-9]+)-([0-9]+)(.*)\.html" /brand.php?id=$1&cat=$2&page=$3 last;
|
||||
rewrite "^/brand-([0-9]+)-c([0-9]+)(.*)\.html" /brand.php?id=$1&cat=$2 last;
|
||||
rewrite "^/brand-([0-9]+)(.*)\.html" /brand.php?id=$1 last;
|
||||
rewrite "^/tag-(.*)\.html" /search.php?keywords=$1 last;
|
||||
rewrite "^/snatch-([0-9]+)\.html$" /snatch.php?id=$1 last;
|
||||
rewrite "^/group_buy-([0-9]+)\.html$" /group_buy.php?act=view&id=$1 last;
|
||||
rewrite "^/auction-([0-9]+)\.html$" /auction.php?act=view&id=$1 last;
|
||||
rewrite "^/exchange-id([0-9]+)(.*)\.html$" /exchange.php?id=$1&act=view last;
|
||||
rewrite "^/exchange-([0-9]+)-min([0-9]+)-max([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /exchange.php?cat_id=$1&integral_min=$2&integral_max=$3&page=$4&sort=$5&order=$6 last;
|
||||
rewrite ^/exchange-([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\.html$" /exchange.php?cat_id=$1&page=$2&sort=$3&order=$4 last;
|
||||
rewrite "^/exchange-([0-9]+)-([0-9]+)(.*)\.html$" /exchange.php?cat_id=$1&page=$2 last;
|
||||
rewrite "^/exchange-([0-9]+)(.*)\.html$" /exchange.php?cat_id=$1 last;
|
||||
}
|
8
cmd/server/nginx_conf/rewrite/edusoho.conf
Normal file
8
cmd/server/nginx_conf/rewrite/edusoho.conf
Normal file
@@ -0,0 +1,8 @@
|
||||
location / {
|
||||
index app.php;
|
||||
try_files $uri @rewriteapp;
|
||||
}
|
||||
|
||||
location @rewriteapp {
|
||||
rewrite ^(.*)$ /app.php/$1 last;
|
||||
}
|
7
cmd/server/nginx_conf/rewrite/emlog.conf
Normal file
7
cmd/server/nginx_conf/rewrite/emlog.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
location / {
|
||||
index index.php index.html;
|
||||
if (!-e $request_filename)
|
||||
{
|
||||
rewrite ^/(.*)$ /index.php last;
|
||||
}
|
||||
}
|
8
cmd/server/nginx_conf/rewrite/empirecms.conf
Normal file
8
cmd/server/nginx_conf/rewrite/empirecms.conf
Normal file
@@ -0,0 +1,8 @@
|
||||
rewrite ^([^\.]*)/listinfo-(.+?)-(.+?)\.html$ $1/e/action/ListInfo/index.php?classid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/showinfo-(.+?)-(.+?)-(.+?)\.html$ $1/e/action/ShowInfo.php?classid=$2&id=$3&page=$4 last;
|
||||
rewrite ^([^\.]*)/infotype-(.+?)-(.+?)\.html$ $1/e/action/InfoType/index.php?ttid=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/tags-(.+?)-(.+?)\.html$ $1/e/tags/index.php?tagname=$2&page=$3 last;
|
||||
rewrite ^([^\.]*)/comment-(.+?)-(.+?)-(.+?)-(.+?)-(.+?)-(.+?)\.html$ $1/e/pl/index\.php\?doaction=$2&classid=$3&id=$4&page=$5&myorder=$6&tempid=$7 last;
|
||||
if (!-e $request_filename) {
|
||||
return 404;
|
||||
}
|
3
cmd/server/nginx_conf/rewrite/laravel5.conf
Normal file
3
cmd/server/nginx_conf/rewrite/laravel5.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
5
cmd/server/nginx_conf/rewrite/maccms.conf
Normal file
5
cmd/server/nginx_conf/rewrite/maccms.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
rewrite ^/vod-(.*)$ /index.php?m=vod-$1 break;
|
||||
rewrite ^/art-(.*)$ /index.php?m=art-$1 break;
|
||||
rewrite ^/gbook-(.*)$ /index.php?m=gbook-$1 break;
|
||||
rewrite ^/label-(.*)$ /index.php?m=label-$1 break;
|
||||
rewrite ^/map-(.*)$ /index.php?m=map-$1 break;
|
6
cmd/server/nginx_conf/rewrite/mvc.conf
Normal file
6
cmd/server/nginx_conf/rewrite/mvc.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
location /{
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^(.*)$ /index.php/$1 last;
|
||||
break;
|
||||
}
|
||||
}
|
6
cmd/server/nginx_conf/rewrite/niushop.conf
Normal file
6
cmd/server/nginx_conf/rewrite/niushop.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^(.*)$ /index.php?s=$1 last;
|
||||
break;
|
||||
}
|
||||
}
|
9
cmd/server/nginx_conf/rewrite/phpcms.conf
Normal file
9
cmd/server/nginx_conf/rewrite/phpcms.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
location / {
|
||||
###以下为PHPCMS 伪静态化rewrite法则
|
||||
rewrite ^(.*)show-([0-9]+)-([0-9]+)\.html$ $1/show.php?itemid=$2&page=$3;
|
||||
rewrite ^(.*)list-([0-9]+)-([0-9]+)\.html$ $1/list.php?catid=$2&page=$3;
|
||||
rewrite ^(.*)show-([0-9]+)\.html$ $1/show.php?specialid=$2;
|
||||
####以下为PHPWind 伪静态化rewrite法则
|
||||
rewrite ^(.*)-htm-(.*)$ $1.php?$2 last;
|
||||
rewrite ^(.*)/simple/([a-z0-9\_]+\.html)$ $1/simple/index.php?$2 last;
|
||||
}
|
4
cmd/server/nginx_conf/rewrite/phpwind.conf
Normal file
4
cmd/server/nginx_conf/rewrite/phpwind.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
location / {
|
||||
rewrite ^(.*)-htm-(.*)$ $1.php?$2 last;
|
||||
rewrite ^(.*)/simple/([a-z0-9\_]+\.html)$ $1/simple/index.php?$2 last;
|
||||
}
|
16
cmd/server/nginx_conf/rewrite/sablog.conf
Normal file
16
cmd/server/nginx_conf/rewrite/sablog.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
location / {
|
||||
rewrite "^/date/([0-9]{6})/?([0-9]+)?/?$" /index.php?action=article&setdate=$1&page=$2 last;
|
||||
rewrite ^/page/([0-9]+)?/?$ /index.php?action=article&page=$1 last;
|
||||
rewrite ^/category/([0-9]+)/?([0-9]+)?/?$ /index.php?action=article&cid=$1&page=$2 last;
|
||||
rewrite ^/category/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&curl=$1&page=$2 last;
|
||||
rewrite ^/(archives|search|article|links)/?$ /index.php?action=$1 last;
|
||||
rewrite ^/(comments|tagslist|trackbacks|article)/?([0-9]+)?/?$ /index.php?action=$1&page=$2 last;
|
||||
rewrite ^/tag/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&item=$1&page=$2 last;
|
||||
rewrite ^/archives/([0-9]+)/?([0-9]+)?/?$ /index.php?action=show&id=$1&page=$2 last;
|
||||
rewrite ^/rss/([0-9]+)?/?$ /rss.php?cid=$1 last;
|
||||
rewrite ^/rss/([^/]+)/?$ /rss.php?url=$1 last;
|
||||
rewrite ^/uid/([0-9]+)/?([0-9]+)?/?$ /index.php?action=article&uid=$1&page=$2 last;
|
||||
rewrite ^/user/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&user=$1&page=$2 last;
|
||||
rewrite sitemap.xml sitemap.php last;
|
||||
rewrite ^(.*)/([0-9a-zA-Z\-\_]+)/?([0-9]+)?/?$ $1/index.php?action=show&alias=$2&page=$3 last;
|
||||
}
|
11
cmd/server/nginx_conf/rewrite/seacms.conf
Normal file
11
cmd/server/nginx_conf/rewrite/seacms.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
location / {
|
||||
rewrite ^/frim/index(.+?)\.html$ /list/index.php?$1 last;
|
||||
rewrite ^/movie/index(.+?)\.html$ /detail/index.php?$1 last;
|
||||
rewrite ^/play/([0-9]+)-([0-9]+)-([0-9]+)\.html$ /video/index.php?$1-$2-$3 last;
|
||||
rewrite ^/topic/index(.+?)\.html$ /topic/index.php?$1 last;
|
||||
rewrite ^/topiclist/index(.+?).html$ /topiclist/index.php?$1 last;
|
||||
rewrite ^/index\.html$ index.php permanent;
|
||||
rewrite ^/news\.html$ news/ permanent;
|
||||
rewrite ^/part/index(.+?)\.html$ /articlelist/index.php?$1 last;
|
||||
rewrite ^/article/index(.+?)\.html$ /article/index.php?$1 last;
|
||||
}
|
5
cmd/server/nginx_conf/rewrite/shopex.conf
Normal file
5
cmd/server/nginx_conf/rewrite/shopex.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^/(.+\.(html|xml|json|htm|php|jsp|asp|shtml))$ /index.php?$1 last;
|
||||
}
|
||||
}
|
14
cmd/server/nginx_conf/rewrite/shopwind.conf
Normal file
14
cmd/server/nginx_conf/rewrite/shopwind.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
location / {
|
||||
#Redirect everything that isn't a real file to index.php
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
#If you want a single domain name at the front and back ends
|
||||
location /admin {
|
||||
try_files $uri $uri/ /admin/index.php$is_args$args;
|
||||
}
|
||||
location /mobile {
|
||||
try_files $uri $uri/ /mobile/index.php$is_args$args;
|
||||
}
|
||||
location /api {
|
||||
try_files $uri $uri/ /api/index.php$is_args$args;
|
||||
}
|
8
cmd/server/nginx_conf/rewrite/thinkphp.conf
Normal file
8
cmd/server/nginx_conf/rewrite/thinkphp.conf
Normal file
@@ -0,0 +1,8 @@
|
||||
location ~* (runtime|application)/ {
|
||||
return 403;
|
||||
}
|
||||
location / {
|
||||
if (!-e $request_filename){
|
||||
rewrite ^(.*)$ /index.php?s=$1 last; break;
|
||||
}
|
||||
}
|
3
cmd/server/nginx_conf/rewrite/typecho.conf
Normal file
3
cmd/server/nginx_conf/rewrite/typecho.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^(.*)$ /index.php$1 last;
|
||||
}
|
5
cmd/server/nginx_conf/rewrite/typecho2.conf
Normal file
5
cmd/server/nginx_conf/rewrite/typecho2.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
location /typecho/ {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^(.*)$ /typecho/index.php$1 last;
|
||||
}
|
||||
}
|
6
cmd/server/nginx_conf/rewrite/wordpress.conf
Normal file
6
cmd/server/nginx_conf/rewrite/wordpress.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
location /
|
||||
{
|
||||
try_files $uri $uri/ /index.php?$args;
|
||||
}
|
||||
|
||||
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
|
6
cmd/server/nginx_conf/rewrite/wp2.conf
Normal file
6
cmd/server/nginx_conf/rewrite/wp2.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
rewrite ^.*/files/(.*)$ /wp-includes/ms-files.php?file=$1 last;
|
||||
if (!-e $request_filename){
|
||||
rewrite ^.+?(/wp-.*) $1 last;
|
||||
rewrite ^.+?(/.*\.php)$ $1 last;
|
||||
rewrite ^ /index.php last;
|
||||
}
|
9
cmd/server/nginx_conf/rewrite/zblog.conf
Normal file
9
cmd/server/nginx_conf/rewrite/zblog.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
if (-f $request_filename/index.html){
|
||||
rewrite (.*) $1/index.html break;
|
||||
}
|
||||
if (-f $request_filename/index.php){
|
||||
rewrite (.*) $1/index.php;
|
||||
}
|
||||
if (!-f $request_filename){
|
||||
rewrite (.*) /index.php;
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
Copyright (c) 2022 Halsey
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
@@ -1 +0,0 @@
|
||||
1Panel 前端
|
@@ -1,544 +0,0 @@
|
||||
# 项目规范文档 📚
|
||||
|
||||
## 一、项目文件、组件命名规范
|
||||
|
||||
> **完全采用 Vue 官方推荐的风格指南,请务必查看 💢**
|
||||
>
|
||||
> **Link:** https://v3.cn.vuejs.org/style-guide
|
||||
|
||||
## 二、代码格式化工具(Prettier)
|
||||
|
||||
### 1、下载安装 prettier:
|
||||
|
||||
```text
|
||||
npm install prettier
|
||||
```
|
||||
|
||||
### 2、安装 Vscode 插件(Prettier):
|
||||
|
||||

|
||||
|
||||
### 3、配置 Prettier:
|
||||
|
||||
```javascript
|
||||
// @see: https://www.prettier.cn
|
||||
|
||||
module.exports = {
|
||||
// 超过最大值换行
|
||||
printWidth: 130,
|
||||
// 缩进字节数
|
||||
tabWidth: 2,
|
||||
// 使用制表符而不是空格缩进行
|
||||
useTabs: true,
|
||||
// 结尾不用分号(true有,false没有)
|
||||
semi: true,
|
||||
// 使用单引号(true单双引号,false双引号)
|
||||
singleQuote: false,
|
||||
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
|
||||
quoteProps: 'as-needed',
|
||||
// 在对象,数组括号与文字之间加空格 "{ foo: bar }"
|
||||
bracketSpacing: true,
|
||||
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
|
||||
trailingComma: 'none',
|
||||
// 在JSX中使用单引号而不是双引号
|
||||
jsxSingleQuote: false,
|
||||
// (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号 ,always:不省略括号
|
||||
arrowParens: 'avoid',
|
||||
// 如果文件顶部已经有一个 doclock,这个选项将新建一行注释,并打上@format标记。
|
||||
insertPragma: false,
|
||||
// 指定要使用的解析器,不需要写文件开头的 @prettier
|
||||
requirePragma: false,
|
||||
// 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行
|
||||
proseWrap: 'preserve',
|
||||
// 在html中空格是否是敏感的 "css" - 遵守CSS显示属性的默认值, "strict" - 空格被认为是敏感的 ,"ignore" - 空格被认为是不敏感的
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
|
||||
endOfLine: 'auto',
|
||||
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
|
||||
rangeStart: 0,
|
||||
rangeEnd: Infinity,
|
||||
// Vue文件脚本和样式标签缩进
|
||||
vueIndentScriptAndStyle: false,
|
||||
};
|
||||
```
|
||||
|
||||
## 三、代码规范工具(ESLint)
|
||||
|
||||
### 1、安装 ESLint 相关插件:
|
||||
|
||||
```text
|
||||
npm install eslint eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser -D
|
||||
```
|
||||
|
||||
| 依赖 | 作用描述 |
|
||||
| :------------------------------: | :------------------------------------------------------------------: |
|
||||
| eslint | ESLint 核心库 |
|
||||
| eslint-config-prettier | 关掉所有和 Prettier 冲突的 ESLint 的配置 |
|
||||
| eslint-plugin-prettier | 将 Prettier 的 rules 以插件的形式加入到 ESLint 里面 |
|
||||
| eslint-plugin-vue | 为 Vue 使用 ESlint 的插件 |
|
||||
| @typescript-eslint/eslint-plugin | ESLint 插件,包含了各类定义好的检测 TypeScript 代码的规范 |
|
||||
| @typescript-eslint/parser | ESLint 的解析器,用于解析 TypeScript,从而检查和规范 TypeScript 代码 |
|
||||
|
||||
### 2、安装 Vscode 插件(ESLint、TSLint):
|
||||
|
||||
- **ESLint:**
|
||||
|
||||

|
||||
|
||||
### 3、配置 ESLint:
|
||||
|
||||
```javascript
|
||||
// @see: http://eslint.cn
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
/* 指定如何解析语法 */
|
||||
parser: 'vue-eslint-parser',
|
||||
/* 优先级低于 parse 的语法解析配置 */
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
jsxPragma: 'React',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
/* 继承某些已有的规则 */
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
/*
|
||||
* "off" 或 0 ==> 关闭规则
|
||||
* "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行)
|
||||
* "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错)
|
||||
*/
|
||||
rules: {
|
||||
// eslint (http://eslint.cn/docs/rules)
|
||||
'no-var': 'error', // 要求使用 let 或 const 而不是 var
|
||||
'no-multiple-empty-lines': ['error', { max: 1 }], // 不允许多个空行
|
||||
'no-use-before-define': 'off', // 禁止在 函数/类/变量 定义之前使用它们
|
||||
'prefer-const': 'off', // 此规则旨在标记使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const
|
||||
'no-irregular-whitespace': 'off', // 禁止不规则的空白
|
||||
|
||||
// typeScript (https://typescript-eslint.io/rules)
|
||||
'@typescript-eslint/no-unused-vars': 'error', // 禁止定义未使用的变量
|
||||
'@typescript-eslint/no-inferrable-types': 'off', // 可以轻松推断的显式类型可能会增加不必要的冗长
|
||||
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间。
|
||||
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
|
||||
'@typescript-eslint/ban-ts-ignore': 'off', // 禁止使用 @ts-ignore
|
||||
'@typescript-eslint/ban-types': 'off', // 禁止使用特定类型
|
||||
'@typescript-eslint/explicit-function-return-type': 'off', // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明
|
||||
'@typescript-eslint/no-var-requires': 'off', // 不允许在 import 语句中使用 require 语句
|
||||
'@typescript-eslint/no-empty-function': 'off', // 禁止空函数
|
||||
'@typescript-eslint/no-use-before-define': 'off', // 禁止在变量定义之前使用它们
|
||||
'@typescript-eslint/ban-ts-comment': 'off', // 禁止 @ts-<directive> 使用注释或要求在指令后进行描述
|
||||
'@typescript-eslint/no-non-null-assertion': 'off', // 不允许使用后缀运算符的非空断言(!)
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off', // 要求导出函数和类的公共类方法的显式返回和参数类型
|
||||
|
||||
// vue (https://eslint.vuejs.org/rules)
|
||||
'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的变量<template>被标记为未使用,此规则仅在启用该no-unused-vars规则时有效。
|
||||
'vue/v-slot-style': 'error', // 强制执行 v-slot 指令样式
|
||||
'vue/no-mutating-props': 'off', // 不允许组件 prop的改变(明天找原因)
|
||||
'vue/custom-event-name-casing': 'off', // 为自定义事件名称强制使用特定大小写
|
||||
'vue/attributes-order': 'off', // vue api使用顺序,强制执行属性顺序
|
||||
'vue/one-component-per-file': 'off', // 强制每个组件都应该在自己的文件中
|
||||
'vue/html-closing-bracket-newline': 'off', // 在标签的右括号之前要求或禁止换行
|
||||
'vue/max-attributes-per-line': 'off', // 强制每行的最大属性数
|
||||
'vue/multiline-html-element-content-newline': 'off', // 在多行元素的内容之前和之后需要换行符
|
||||
'vue/singleline-html-element-content-newline': 'off', // 在单行元素的内容之前和之后需要换行符
|
||||
'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式
|
||||
'vue/require-default-prop': 'off', // 此规则要求为每个 prop 为必填时,必须提供默认值
|
||||
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 四、样式规范工具(StyleLint)
|
||||
|
||||
### 1、安装 StyleLint 相关插件:
|
||||
|
||||
```text
|
||||
npm i stylelint stylelint-config-html stylelint-config-recommended-scss stylelint-config-recommended-vue stylelint-config-standard stylelint-config-standard-scss stylelint-config-recess-order postcss postcss-html stylelint-config-prettier -D
|
||||
```
|
||||
|
||||
| 依赖 | 作用描述 |
|
||||
| :-------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| stylelint | stylelint 核心库 |
|
||||
| stylelint-config-html | Stylelint 的可共享 HTML(和类似 HTML)配置,捆绑 postcss-html 并对其进行配置。 |
|
||||
| stylelint-config-recommended-scss | 扩展 stylelint-config-recommended 共享配置,并为 SCSS 配置其规则 |
|
||||
| stylelint-config-recommended-vue | 扩展 stylelint-config-recommended 共享配置,并为 Vue 配置其规则 |
|
||||
| stylelint-config-standard | 打开额外的规则来执行在规范和一些 CSS 样式指南中发现的通用约定,包括:惯用 CSS 原则,谷歌的 CSS 样式指南,Airbnb 的样式指南,和 @mdo 的代码指南。 |
|
||||
| stylelint-config-standard-scss | 扩展 stylelint-config-standard 共享配置,并为 SCSS 配置其规则 |
|
||||
| postcss | postcss-html 的依赖包 |
|
||||
| postcss-html | 用于解析 HTML(和类似 HTML)的 PostCSS 语法 |
|
||||
| stylelint-config-recess-order | 属性的排序(插件包) |
|
||||
| stylelint-config-prettier | 关闭所有不必要的或可能与 Prettier 冲突的规则 |
|
||||
|
||||
### 2、安装 Vscode 插件(Stylelint):
|
||||
|
||||

|
||||
|
||||
### 3、在目录的 .vscode 文件中新建 settings.json:
|
||||
|
||||
```json
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"stylelint.enable": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.stylelint": true
|
||||
},
|
||||
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass", "html"],
|
||||
"files.eol": "\n"
|
||||
}
|
||||
```
|
||||
|
||||
> 😎 也可以在 vscode 中全局配置上述 json 代码 😎
|
||||
|
||||
### 4、配置 stylelint.config.js
|
||||
|
||||
```javascript
|
||||
// @see: https://stylelint.io
|
||||
|
||||
module.exports = {
|
||||
/* 继承某些已有的规则 */
|
||||
extends: [
|
||||
'stylelint-config-standard', // 配置stylelint拓展插件
|
||||
'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化
|
||||
'stylelint-config-standard-scss', // 配置stylelint scss插件
|
||||
'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 样式格式化
|
||||
'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,
|
||||
'stylelint-config-prettier', // 配置stylelint和prettier兼容
|
||||
],
|
||||
overrides: [
|
||||
// 扫描 .vue/html 文件中的<style>标签内的样式
|
||||
{
|
||||
files: ['**/*.{vue,html}'],
|
||||
customSyntax: 'postcss-html',
|
||||
},
|
||||
],
|
||||
/**
|
||||
* null => 关闭该规则
|
||||
*/
|
||||
rules: {
|
||||
'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
|
||||
'function-url-quotes': 'always', // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"
|
||||
'string-quotes': 'double', // 指定字符串使用单引号或双引号
|
||||
'unit-case': null, // 指定单位的大小写 "lower(全小写)"|"upper(全大写)"
|
||||
'color-hex-case': 'lower', // 指定 16 进制颜色的大小写 "lower(全小写)"|"upper(全大写)"
|
||||
'color-hex-length': 'long', // 指定 16 进制颜色的简写或扩写 "short(16进制简写)"|"long(16进制扩写)"
|
||||
'rule-empty-line-before': 'never', // 要求或禁止在规则之前的空行 "always(规则之前必须始终有一个空行)"|"never(规则前绝不能有空行)"|"always-multi-line(多行规则之前必须始终有一个空行)"|"never-multi-line(多行规则之前绝不能有空行。)"
|
||||
'font-family-no-missing-generic-family-keyword': null, // 禁止在字体族名称列表中缺少通用字体族关键字
|
||||
'block-opening-brace-space-before': 'always', // 要求在块的开大括号之前必须有一个空格或不能有空白符 "always(大括号前必须始终有一个空格)"|"never(左大括号之前绝不能有空格)"|"always-single-line(在单行块中的左大括号之前必须始终有一个空格)"|"never-single-line(在单行块中的左大括号之前绝不能有空格)"|"always-multi-line(在多行块中,左大括号之前必须始终有一个空格)"|"never-multi-line(多行块中的左大括号之前绝不能有空格)"
|
||||
'property-no-unknown': null, // 禁止未知的属性(true 为不允许)
|
||||
'no-empty-source': null, // 禁止空源码
|
||||
'declaration-block-trailing-semicolon': null, // 要求或不允许在声明块中使用尾随分号 string:"always(必须始终有一个尾随分号)"|"never(不得有尾随分号)"
|
||||
'selector-class-pattern': null, // 强制选择器类名的格式
|
||||
'scss/at-import-partial-extension': null, // 解决不能引入scss文件
|
||||
'value-no-vendor-prefix': null, // 关闭 vendor-prefix(为了解决多行省略 -webkit-box)
|
||||
'selector-pseudo-class-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoClasses: ['global', 'v-deep', 'deep'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 五、EditorConfig 配置
|
||||
|
||||
### 1、简介
|
||||
|
||||
> **EditorConfig** 帮助开发人员在 **不同的编辑器** 和 **IDE** 之间定义和维护一致的编码样式。
|
||||
|
||||
### 2、安装 VsCode 插件(EditorConfig ):
|
||||
|
||||

|
||||
|
||||
### 3、配置 EditorConfig:
|
||||
|
||||
```javascript
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*] # 表示所有文件适用
|
||||
charset = utf-8 # 设置文件字符集为 utf-8
|
||||
end_of_line = lf # 控制换行类型(lf | cr | crlf)
|
||||
insert_final_newline = true # 始终在文件末尾插入一个新行
|
||||
indent_style = tab # 缩进风格(tab | space)
|
||||
indent_size = 2 # 缩进大小
|
||||
max_line_length = 130 # 最大行长度
|
||||
|
||||
[*.md] # 表示仅 md 文件适用以下规则
|
||||
max_line_length = off # 关闭最大行长度限制
|
||||
trim_trailing_whitespace = false # 关闭末尾空格修剪
|
||||
```
|
||||
|
||||
## 六、Git 流程规范配置
|
||||
|
||||
| 依赖 | 作用描述 |
|
||||
| :-----------------------------: | :----------------------------------------------------------------------------: |
|
||||
| husky | 操作 **git** 钩子的工具(在 **git xx** 之前执行某些命令) |
|
||||
| lint-staged | 在提交之前进行 **eslint** 校验,并使用 **prettier** 格式化本地暂存区的代码, |
|
||||
| @commitlint/cli | 校验 **git commit** 信息是否符合规范,保证团队的一致性 |
|
||||
| @commitlint/config-conventional | **Anglar** 的提交规范 |
|
||||
| commitizen | 基于 **Node.js** 的 **git commit** 命令行工具,生成标准化的 **commit message** |
|
||||
| cz-git | 一款工程性更强,轻量级,高度自定义,标准输出格式的 **commitize** 适配器 |
|
||||
|
||||
### 1、husky(操作 git 钩子的工具):
|
||||
|
||||
> **安装:**
|
||||
|
||||
```text
|
||||
npm install husky -D
|
||||
```
|
||||
|
||||
> **使用(为了添加.husky 文件夹):**
|
||||
|
||||
```text
|
||||
# 编辑 package.json > prepare 脚本并运行一次
|
||||
|
||||
npm set-script prepare "husky install"
|
||||
npm run prepare
|
||||
```
|
||||
|
||||
### 2、 lint-staged(本地暂存代码检查工具)
|
||||
|
||||
> **安装:**
|
||||
|
||||
```text
|
||||
npm install lint-staged --D
|
||||
```
|
||||
|
||||
> **添加 ESlint Hook(在.husky 文件夹下添加 pre-commit 文件):**
|
||||
>
|
||||
> **作用:通过钩子函数,判断提交的代码是否符合规范,并使用 prettier 格式化代码**
|
||||
|
||||
```text
|
||||
npx husky add .husky/pre-commit "npm run lint:lint-staged"
|
||||
```
|
||||
|
||||
> 新增 **lint-staged.config.js** 文件:
|
||||
|
||||
```text
|
||||
module.exports = {
|
||||
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
|
||||
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": ["prettier --write--parser json"],
|
||||
"package.json": ["prettier --write"],
|
||||
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
|
||||
"*.{scss,less,styl,html}": ["stylelint --fix", "prettier --write"],
|
||||
"*.md": ["prettier --write"]
|
||||
};
|
||||
```
|
||||
|
||||
### 3、commitlint(commit 信息校验工具,不符合则报错)
|
||||
|
||||
> **安装:**
|
||||
|
||||
```text
|
||||
npm i @commitlint/cli @commitlint/config-conventional -D
|
||||
```
|
||||
|
||||
> **配置命令(在.husky 文件夹下添加 commit-msg 文件):**
|
||||
|
||||
```text
|
||||
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
|
||||
```
|
||||
|
||||
### 4、commitizen(基于 Node.js 的 git commit 命令行工具,生成标准化的 message)
|
||||
|
||||
```text
|
||||
// 安装 commitizen,如此一来可以快速使用 cz 或 git cz 命令进行启动。
|
||||
npm install commitizen -D
|
||||
```
|
||||
|
||||
### 5、cz-git
|
||||
|
||||
> **指定提交文字规范,一款工程性更强,高度自定义,标准输出格式的 commitizen 适配器**
|
||||
|
||||
```text
|
||||
npm install cz-git -D
|
||||
```
|
||||
|
||||
> **配置 package.json:**
|
||||
|
||||
```text
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **新建 commitlint.config.js 文件:**
|
||||
|
||||
```javascript
|
||||
// @see: https://cz-git.qbenben.com/zh/guide
|
||||
/** @type {import('cz-git').UserConfig} */
|
||||
|
||||
module.exports = {
|
||||
ignores: [(commit) => commit.includes('init')],
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
// @see: https://commitlint.js.org/#/reference-rules
|
||||
'body-leading-blank': [2, 'always'],
|
||||
'footer-leading-blank': [1, 'always'],
|
||||
'header-max-length': [2, 'always', 108],
|
||||
'subject-empty': [2, 'never'],
|
||||
'type-empty': [2, 'never'],
|
||||
'subject-case': [0],
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat',
|
||||
'fix',
|
||||
'docs',
|
||||
'style',
|
||||
'refactor',
|
||||
'perf',
|
||||
'test',
|
||||
'build',
|
||||
'ci',
|
||||
'chore',
|
||||
'revert',
|
||||
'wip',
|
||||
'workflow',
|
||||
'types',
|
||||
'release',
|
||||
],
|
||||
],
|
||||
},
|
||||
prompt: {
|
||||
messages: {
|
||||
type: "Select the type of change that you're committing:",
|
||||
scope: 'Denote the SCOPE of this change (optional):',
|
||||
customScope: 'Denote the SCOPE of this change:',
|
||||
subject: 'Write a SHORT, IMPERATIVE tense description of the change:\n',
|
||||
body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
|
||||
breaking: 'List any BREAKING CHANGES (optional). Use "|" to break new line:\n',
|
||||
footerPrefixsSelect: 'Select the ISSUES type of changeList by this change (optional):',
|
||||
customFooterPrefixs: 'Input ISSUES prefix:',
|
||||
footer: 'List any ISSUES by this change. E.g.: #31, #34:\n',
|
||||
confirmCommit: 'Are you sure you want to proceed with the commit above?',
|
||||
// 中文版
|
||||
// type: "选择你要提交的类型 :",
|
||||
// scope: "选择一个提交范围(可选):",
|
||||
// customScope: "请输入自定义的提交范围 :",
|
||||
// subject: "填写简短精炼的变更描述 :\n",
|
||||
// body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
|
||||
// breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
|
||||
// footerPrefixsSelect: "选择关联issue前缀(可选):",
|
||||
// customFooterPrefixs: "输入自定义issue前缀 :",
|
||||
// footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
|
||||
// confirmCommit: "是否提交或修改commit ?"
|
||||
},
|
||||
types: [
|
||||
{
|
||||
value: 'feat',
|
||||
name: 'feat: 🚀 A new feature',
|
||||
emoji: '🚀',
|
||||
},
|
||||
{
|
||||
value: 'fix',
|
||||
name: 'fix: 🧩 A bug fix',
|
||||
emoji: '🧩',
|
||||
},
|
||||
{
|
||||
value: 'docs',
|
||||
name: 'docs: 📚 Documentation only changes',
|
||||
emoji: '📚',
|
||||
},
|
||||
{
|
||||
value: 'style',
|
||||
name: 'style: 🎨 Changes that do not affect the meaning of the code',
|
||||
emoji: '🎨',
|
||||
},
|
||||
{
|
||||
value: 'refactor',
|
||||
name: 'refactor: ♻️ A code change that neither fixes a bug nor adds a feature',
|
||||
emoji: '♻️',
|
||||
},
|
||||
{
|
||||
value: 'perf',
|
||||
name: 'perf: ⚡️ A code change that improves performance',
|
||||
emoji: '⚡️',
|
||||
},
|
||||
{
|
||||
value: 'test',
|
||||
name: 'test: ✅ Adding missing tests or correcting existing tests',
|
||||
emoji: '✅',
|
||||
},
|
||||
{
|
||||
value: 'build',
|
||||
name: 'build: 📦️ Changes that affect the build system or external dependencies',
|
||||
emoji: '📦️',
|
||||
},
|
||||
{
|
||||
value: 'ci',
|
||||
name: 'ci: 🎡 Changes to our CI configuration files and scripts',
|
||||
emoji: '🎡',
|
||||
},
|
||||
{
|
||||
value: 'chore',
|
||||
name: "chore: 🔨 Other changes that don't modify src or test files",
|
||||
emoji: '🔨',
|
||||
},
|
||||
{
|
||||
value: 'revert',
|
||||
name: 'revert: ⏪️ Reverts a previous commit',
|
||||
emoji: '⏪️',
|
||||
},
|
||||
// 中文版
|
||||
// { value: "特性", name: "特性: 🚀 新增功能", emoji: "🚀" },
|
||||
// { value: "修复", name: "修复: 🧩 修复缺陷", emoji: "🧩" },
|
||||
// { value: "文档", name: "文档: 📚 文档变更", emoji: "📚" },
|
||||
// { value: "格式", name: "格式: 🎨 代码格式(不影响功能,例如空格、分号等格式修正)", emoji: "🎨" },
|
||||
// { value: "重构", name: "重构: ♻️ 代码重构(不包括 bug 修复、功能新增)", emoji: "♻️" },
|
||||
// { value: "性能", name: "性能: ⚡️ 性能优化", emoji: "⚡️" },
|
||||
// { value: "测试", name: "测试: ✅ 添加疏漏测试或已有测试改动", emoji: "✅" },
|
||||
// { value: "构建", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)", emoji: "📦️" },
|
||||
// { value: "集成", name: "集成: 🎡 修改 CI 配置、脚本", emoji: "🎡" },
|
||||
// { value: "回退", name: "回退: ⏪️ 回滚 commit", emoji: "⏪️" },
|
||||
// { value: "其他", name: "其他: 🔨 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: "🔨" }
|
||||
],
|
||||
useEmoji: true,
|
||||
themeColorCode: '',
|
||||
scopes: [],
|
||||
allowCustomScopes: true,
|
||||
allowEmptyScopes: true,
|
||||
customScopesAlign: 'bottom',
|
||||
customScopesAlias: 'custom',
|
||||
emptyScopesAlias: 'empty',
|
||||
upperCaseSubject: false,
|
||||
allowBreakingChanges: ['feat', 'fix'],
|
||||
breaklineNumber: 100,
|
||||
breaklineChar: '|',
|
||||
skipQuestions: [],
|
||||
issuePrefixs: [{ value: 'closed', name: 'closed: ISSUES has been processed' }],
|
||||
customIssuePrefixsAlign: 'top',
|
||||
emptyIssuePrefixsAlias: 'skip',
|
||||
customIssuePrefixsAlias: 'custom',
|
||||
allowCustomIssuePrefixs: true,
|
||||
allowEmptyIssuePrefixs: true,
|
||||
confirmColorize: true,
|
||||
maxHeaderLength: Infinity,
|
||||
maxSubjectLength: Infinity,
|
||||
minSubjectLength: 0,
|
||||
scopeOverrides: undefined,
|
||||
defaultBody: '',
|
||||
defaultIssues: '',
|
||||
defaultScope: '',
|
||||
defaultSubject: '',
|
||||
},
|
||||
};
|
||||
```
|
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@@ -61,6 +61,7 @@ declare module 'vue' {
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSpace: typeof import('element-plus/es')['ElSpace']
|
||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
|
@@ -130,6 +130,7 @@ export namespace App {
|
||||
export interface DatabaseConnInfo {
|
||||
password: string;
|
||||
serviceName: string;
|
||||
port: number;
|
||||
}
|
||||
export interface AppInstallResource {
|
||||
type: string;
|
||||
|
@@ -17,6 +17,9 @@ export namespace Website {
|
||||
appinstall?: NewAppInstall;
|
||||
webSiteSSL: SSL;
|
||||
runtimeID: number;
|
||||
rewrite: string;
|
||||
user: string;
|
||||
group: string;
|
||||
}
|
||||
|
||||
export interface WebsiteDTO extends Website {
|
||||
@@ -278,4 +281,30 @@ export namespace Website {
|
||||
content: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface RewriteReq {
|
||||
websiteID: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface RewriteRes {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface RewriteUpdate {
|
||||
websiteID: number;
|
||||
name: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface DirUpdate {
|
||||
id: number;
|
||||
siteDir: string;
|
||||
}
|
||||
|
||||
export interface DirPermissionUpdate {
|
||||
id: number;
|
||||
user: string;
|
||||
group: string;
|
||||
}
|
||||
}
|
||||
|
@@ -98,7 +98,7 @@ export const editBackup = (params: Backup.BackupOperate) => {
|
||||
}
|
||||
return http.post(`/settings/backup/update`, reqest);
|
||||
};
|
||||
export const deleteBackup = (params: { ids: number[] }) => {
|
||||
export const deleteBackup = (params: { id: number }) => {
|
||||
return http.post(`/settings/backup/del`, params);
|
||||
};
|
||||
export const listBucket = (params: Backup.ForBucket) => {
|
||||
|
@@ -170,3 +170,19 @@ export const UpdatePHPConfig = (req: Website.PHPConfigUpdate) => {
|
||||
export const UpdatePHPFile = (req: Website.PHPUpdate) => {
|
||||
return http.post<any>(`/websites/php/update`, req);
|
||||
};
|
||||
|
||||
export const GetRewriteConfig = (req: Website.RewriteReq) => {
|
||||
return http.post<Website.RewriteRes>(`/websites/rewrite`, req);
|
||||
};
|
||||
|
||||
export const UpdateRewriteConfig = (req: Website.RewriteUpdate) => {
|
||||
return http.post<any>(`/websites/rewrite/update`, req);
|
||||
};
|
||||
|
||||
export const UpdateWebsiteDir = (req: Website.DirUpdate) => {
|
||||
return http.post<any>(`/websites/dir/update`, req);
|
||||
};
|
||||
|
||||
export const UpdateWebsiteDirPermission = (req: Website.DirPermissionUpdate) => {
|
||||
return http.post<any>(`/websites/dir/permission`, req);
|
||||
};
|
||||
|
@@ -1,9 +1,9 @@
|
||||
@font-face {
|
||||
font-family: "panel"; /* Project id 3575356 */
|
||||
src: url('iconfont.woff2?t=1680702361679') format('woff2'),
|
||||
url('iconfont.woff?t=1680702361679') format('woff'),
|
||||
url('iconfont.ttf?t=1680702361679') format('truetype'),
|
||||
url('iconfont.svg?t=1680702361679#panel') format('svg');
|
||||
src: url('iconfont.woff2?t=1681715760705') format('woff2'),
|
||||
url('iconfont.woff?t=1681715760705') format('woff'),
|
||||
url('iconfont.ttf?t=1681715760705') format('truetype'),
|
||||
url('iconfont.svg?t=1681715760705#panel') format('svg');
|
||||
}
|
||||
|
||||
.panel {
|
||||
@@ -14,6 +14,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.p-yanzhengma1:before {
|
||||
content: "\e744";
|
||||
}
|
||||
|
||||
.p-tengxunyun1:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -5,6 +5,13 @@
|
||||
"css_prefix_text": "p-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "7131916",
|
||||
"name": "验证码",
|
||||
"font_class": "yanzhengma1",
|
||||
"unicode": "e744",
|
||||
"unicode_decimal": 59204
|
||||
},
|
||||
{
|
||||
"icon_id": "12959160",
|
||||
"name": "腾讯云",
|
||||
|
@@ -14,6 +14,8 @@
|
||||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="yanzhengma1" unicode="" d="M544 627h124c17.673 0 32-14.327 32-32 0-17.673-14.327-32-32-32H544v-116a32.5 32.5 0 0 0-0.056-1.916C670.218 429.588 768 321.96299999999997 768 191.5 768 50.39099999999996 653.609-64 512.5-64 371.391-64 257 50.39099999999996 257 191.5c0 130.12 97.27 237.523 223.064 253.46A32.488 32.488 0 0 0 480 447V800c0 17.673 14.327 32 32 32 17.673 0 32-14.327 32-32v-45h192c17.673 0 32-14.327 32-32 0-17.673-14.327-32-32-32H544v-64z m-31.5-627C618.263 0 704 85.73699999999997 704 191.5S618.263 383 512.5 383 321 297.26300000000003 321 191.5 406.737 0 512.5 0z" horiz-adv-x="1024" />
|
||||
|
||||
<glyph glyph-name="tengxunyun1" unicode="" d="M512 725.333333c130.474667 0 240.938667-83.797333 277.930667-199.296a198.826667 198.826667 0 0 1-41.557334 0.597334 222.293333 222.293333 0 0 1-49.706666-10.624C668.202667 586.666667 596.096 636.245333 512 636.245333c-100.266667 0-183.466667-70.528-199.381333-163.029333a279.04 279.04 0 0 1-89.429334 3.84C241.28 617.045333 363.690667 725.333333 512 725.333333zM258.474667 478.677333c54.442667 0 104.192-20.181333 142.165333-53.418666 16.085333-14.08 45.226667-39.68 87.381333-76.8l-7.381333 6.528-61.568-60.885334-54.4 54.4c-34.218667 34.261333-66.090667 47.957333-106.197333 47.957334a133.589333 133.589333 0 0 1 0-267.221334c10.666667 0 29.312-0.768 56.064-2.346666l-90.453334-77.141334A215.893333 215.893333 0 0 0 258.432 478.72zM674.346667 461.525333a215.808 215.808 0 0 0 168.618666-397.354666c-15.36-6.485333-38.186667-15.957333-63.146666-16.213334-72.106667-0.597333-244.181333-0.896-516.352-0.938666h-42.666667a206248.106667 206248.106667 0 0 1 397.013333 380.714666c18.261333 17.578667 41.130667 27.264 56.533334 33.792z m41.856-80.554666c-9.258667-3.925333-23.04-9.770667-34.048-20.352-30.165333-29.098667-109.952-105.642667-239.445334-229.632h53.418667c148.181333 0 242.773333 0.213333 283.733333 0.554666 15.061333 0.128 28.842667 5.845333 38.101334 9.813334a130.133333 130.133333 0 0 1-101.76 239.616z" horiz-adv-x="1024" />
|
||||
|
||||
<glyph glyph-name="qiniuyun" unicode="" d="M512-42.667c235.648 0 426.667 191.019 426.667 426.667S747.648 810.667 512 810.667 85.333 619.648 85.333 384 276.352-42.667 512-42.667z m290.73 597.078c3.969 0.896 6.145-0.854 7.169-2.688 1.664-2.987 0.085-7.168 0.085-7.168-42.155-120.192-158.293-202.07-297.941-201.515h-0.086a339.883 339.883 0 0 0-81.365 9.43l35.541-119.723s1.963-16.342 20.95-16.342h57.002s24.875-1.92 30.464 19.328c5.334 20.182 25.6 67.414 25.6 67.414s6.315 19.2 25.088 32.512c18.774 13.312 28.032 13.013 29.227 12.288 0.981-0.64 0.64-2.176 0.64-2.176l-19.499-113.536c-2.176-37.078-28.8-61.568-64.341-61.568H464.939c-35.499 0-62.848 22.4-64.299 61.568l-21.376 137.301c-77.568 33.28-137.472 95.83-165.248 175.019 0 0-1.579 4.224 0.085 7.168 1.024 1.877 3.2 3.584 7.168 2.688 1.152-0.256 4.182-3.755 9.174-9.472 18.773-21.632 65.066-75.094 142.848-106.966l-7.979 56.747s-0.17 1.28 0.81 2.432c1.067 1.28 6.273 2.688 15.19-1.792a32.213 32.213 0 0 0 15.957-19.328l15.702-51.499a351.147 351.147 0 0 1 98.986-11.733h0.086c164.01-3.37 253.525 99.84 281.514 132.139 4.992 5.717 8.022 9.216 9.174 9.472z" horiz-adv-x="1024" />
|
||||
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 65 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
</div>
|
||||
<div class="footer flx-justify-between">
|
||||
<div class="footer-left">
|
||||
<a href="https://fit2cloud.com/" target="_blank">Copyright @2014-2023 FIT2CLOUND 飞致云</a>
|
||||
<a href="https://fit2cloud.com/" target="_blank">Copyright © 2014-2023 FIT2CLOUD 飞致云</a>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<SystemUpgrade />
|
||||
|
@@ -161,27 +161,3 @@ onMounted(() => {
|
||||
onCheck();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.app-warn {
|
||||
text-align: center;
|
||||
margin-top: 100px;
|
||||
span:first-child {
|
||||
color: #bbbfc4;
|
||||
}
|
||||
|
||||
span:nth-child(2) {
|
||||
color: $primary-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span:nth-child(2):hover {
|
||||
color: #74a4f3;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -18,7 +18,7 @@
|
||||
<span v-if="row.isDefault">({{ $t('website.default') }})</span>
|
||||
</div>
|
||||
|
||||
<el-form ref="groupForm" v-if="row.edit" :model="row">
|
||||
<el-form @submit.prevent ref="groupForm" v-if="row.edit" :model="row">
|
||||
<el-form-item prop="name" v-if="row.edit" :rules="Rules.name">
|
||||
<div style="margin-top: 20px; width: 100%"><el-input v-model="row.name" /></div>
|
||||
</el-form-item>
|
||||
|
@@ -1,10 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<span class="version">{{ version }}</span>
|
||||
<el-button v-if="version !== 'Waiting'" style="margin-top: -2px" type="primary" link @click="onLoadUpgradeInfo">
|
||||
<el-badge is-dot class="item" v-if="version !== 'Waiting' && globalStore.hasNewVersion">
|
||||
<el-button style="margin-top: -8px" type="primary" link @click="onLoadUpgradeInfo">
|
||||
{{ $t('setting.upgradeCheck') }}
|
||||
</el-button>
|
||||
</el-badge>
|
||||
<el-button
|
||||
v-if="version !== 'Waiting' && !globalStore.hasNewVersion"
|
||||
style="margin-top: -2px"
|
||||
type="primary"
|
||||
link
|
||||
@click="onLoadUpgradeInfo"
|
||||
>
|
||||
{{ $t('setting.upgradeCheck') }}
|
||||
</el-button>
|
||||
<el-tag v-else round style="margin-left: 10px">{{ $t('setting.upgrading') }}</el-tag>
|
||||
<el-tag v-if="version === 'Waiting'" round style="margin-left: 10px">{{ $t('setting.upgrading') }}</el-tag>
|
||||
</div>
|
||||
|
||||
<el-drawer :close-on-click-modal="false" :key="refresh" v-model="drawerVisiable" size="50%" append-to-body>
|
||||
|
@@ -78,3 +78,35 @@ export const Languages = [
|
||||
value: ['css'],
|
||||
},
|
||||
];
|
||||
|
||||
export const Rewrites = [
|
||||
'default',
|
||||
'wordpress',
|
||||
'wp2',
|
||||
'typecho',
|
||||
'typecho2',
|
||||
'thinkphp',
|
||||
'laravel5',
|
||||
'discuz',
|
||||
'discuzx',
|
||||
'discuzx2',
|
||||
'discuzx3',
|
||||
'EduSoho',
|
||||
'EmpireCMS',
|
||||
'ShopWind',
|
||||
'crmeb',
|
||||
'dabr',
|
||||
'dbshop',
|
||||
'dedecms',
|
||||
'drupal',
|
||||
'ecshop',
|
||||
'emlog',
|
||||
'maccms',
|
||||
'mvc',
|
||||
'niushop',
|
||||
'phpcms',
|
||||
'sablog',
|
||||
'seacms',
|
||||
'shopex',
|
||||
'zblog',
|
||||
];
|
||||
|
@@ -10,6 +10,7 @@ const i18n = createI18n({
|
||||
zh,
|
||||
en,
|
||||
},
|
||||
warnHtmlMessage: false,
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
@@ -128,7 +128,7 @@ const message = {
|
||||
mfaCode: 'MFA verification code',
|
||||
title: 'Linux Server Management Panel',
|
||||
licenseHelper:
|
||||
'Agree to FIT2CLOUD « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Community Software License Agreement</a> »',
|
||||
'Agree « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Community License Agreement</a> »',
|
||||
errorAgree: 'Please click to agree to the Community Software License Agreement',
|
||||
},
|
||||
rule: {
|
||||
@@ -422,6 +422,15 @@ const message = {
|
||||
rdbHelper2: 'The data',
|
||||
rdbHelper3: 'Meeting either condition triggers RDB persistence',
|
||||
rdbInfo: 'Ensure that the value in the rule list ranges from 1 to 100000',
|
||||
|
||||
containerConn: 'Container connection address',
|
||||
containerConnHelper: 'PHP runtime environment/container-installed applications use this connection address',
|
||||
remoteConn: 'External connection address',
|
||||
remoteConnHelper2: 'Use this address for non-container or external connections',
|
||||
localIP: 'Local IP',
|
||||
userGroup: 'User/Group',
|
||||
user: 'User',
|
||||
uGroup: 'Group',
|
||||
},
|
||||
container: {
|
||||
createContainer: 'Create container',
|
||||
@@ -1166,6 +1175,16 @@ const message = {
|
||||
updateConfig: 'Update Config',
|
||||
isOn: 'On',
|
||||
isOff: 'Off',
|
||||
rewrite: 'Pseudo Static',
|
||||
rewriteMode: 'Scheme',
|
||||
current: 'Current',
|
||||
rewriteHelper:
|
||||
'If the website cannot be accessed normally after setting pseudo-static, please try to set it back to default',
|
||||
runDir: 'Run Directory',
|
||||
runDirHelper:
|
||||
'Some programs need to specify a secondary directory as the running directory, such as ThinkPHP5, Laravel',
|
||||
runUserHelper:
|
||||
'For websites deployed through the PHP runtime environment, all files, folder owners, and user groups under the index and subdirectories need to be set to 1000, command: chown -R 1000:1000 index',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: 'Short tag support',
|
||||
@@ -1227,6 +1246,8 @@ const message = {
|
||||
'This certificate has been associated with the following websites, and the renewal will be applied to these websites simultaneously',
|
||||
},
|
||||
firewall: {
|
||||
notSupport:
|
||||
'No system firewall detected (firewalld or ufw). Please refer to the official documentation for installation.',
|
||||
ccDeny: 'CC Protection',
|
||||
ipWhiteList: 'IP Whitelist',
|
||||
ipBlockList: 'IP Blacklist',
|
||||
@@ -1301,7 +1322,7 @@ const message = {
|
||||
status: 'Status',
|
||||
versionHelper: 'PHP version, e.g. v8.0',
|
||||
buildHelper:
|
||||
'The more extensions you select, the more CPU will be occupied during the image making process, so avoid selecting all extensions',
|
||||
'The more extensions you select, the more CPU will be occupied during the image making process, so avoid selecting all extensions,If there is no extension you want, you can manually enter it and select it',
|
||||
openrestryWarn: 'PHP needs to be upgraded to OpenResty to version 1.21.4.1 or later to use',
|
||||
toupgrade: 'To Upgrade',
|
||||
},
|
||||
|
@@ -431,6 +431,12 @@ const message = {
|
||||
rdbHelper2: '条数据',
|
||||
rdbHelper3: '符合任意一个条件将会触发RDB持久化',
|
||||
rdbInfo: '请确认规则列表中值在 1-100000 之间',
|
||||
|
||||
containerConn: '容器连接地址',
|
||||
containerConnHelper: 'PHP 运行环境/容器安装的应用使用此连接地址',
|
||||
remoteConn: '外部连接地址',
|
||||
remoteConnHelper2: '非容器或外部连接使用此地址',
|
||||
localIP: '本机 IP',
|
||||
},
|
||||
container: {
|
||||
createContainer: '创建容器',
|
||||
@@ -1159,6 +1165,17 @@ const message = {
|
||||
updateConfig: '配置修改',
|
||||
isOn: '开启',
|
||||
isOff: '关闭',
|
||||
rewrite: '伪静态',
|
||||
rewriteMode: '方案',
|
||||
current: '当前',
|
||||
rewriteHelper: '若设置伪静态后,网站无法正常访问,请尝试设置回default',
|
||||
runDir: '运行目录',
|
||||
runDirHelper: '部分程序需要指定二级目录作为运行目录,如ThinkPHP5,Laravel',
|
||||
runUserHelper:
|
||||
'通过 PHP 运行环境部署的网站,需要将 index 和子目录下的所有文件、文件夹所有者和用户组设置为 1000',
|
||||
userGroup: '运行用户/组',
|
||||
user: '用户',
|
||||
uGroup: '用户组',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: '短标签支持',
|
||||
@@ -1220,6 +1237,7 @@ const message = {
|
||||
createAcme: '创建账户',
|
||||
},
|
||||
firewall: {
|
||||
notSupport: '未检测到系统防火墙(firewalld 或者 ufw),请参考官方文档进行安装',
|
||||
ccDeny: 'CC 防护',
|
||||
ipWhiteList: 'IP 白名单',
|
||||
ipBlockList: 'IP 黑名单',
|
||||
@@ -1283,7 +1301,8 @@ const message = {
|
||||
version: '版本',
|
||||
status: '状态',
|
||||
versionHelper: 'PHP的版本,例如 v8.0',
|
||||
buildHelper: '选择的扩展越多,制作镜像过程中占用 CPU 越多,请尽量避免选择全部扩展',
|
||||
buildHelper:
|
||||
'选择的扩展越多,制作镜像过程中占用 CPU 越多,请尽量避免选择全部扩展,如果没有想要的扩展,可以手动输入之后选择',
|
||||
openrestryWarn: 'PHP 需要升级 OpenResty 至 1.21.4.1 版本以上才能使用',
|
||||
toupgrade: '去升级',
|
||||
},
|
||||
|
@@ -21,6 +21,7 @@ export const GlobalStore = defineStore({
|
||||
},
|
||||
isFullScreen: false,
|
||||
agreeLicense: false,
|
||||
hasNewVersion: false,
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
|
@@ -17,6 +17,7 @@ export interface GlobalState {
|
||||
themeConfig: ThemeConfigProp;
|
||||
isFullScreen: boolean;
|
||||
agreeLicense: boolean;
|
||||
hasNewVersion: boolean;
|
||||
}
|
||||
|
||||
export interface MenuState {
|
||||
|
@@ -302,3 +302,25 @@
|
||||
.table-link:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.app-warn {
|
||||
text-align: center;
|
||||
margin-top: 100px;
|
||||
span:first-child {
|
||||
color: #bbbfc4;
|
||||
}
|
||||
|
||||
span:nth-child(2) {
|
||||
color: $primary-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span:nth-child(2):hover {
|
||||
color: #74a4f3;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
@@ -13,6 +13,7 @@
|
||||
<el-row v-loading="loading">
|
||||
<el-col :span="22" :offset="1">
|
||||
<el-form
|
||||
@submit.prevent
|
||||
ref="paramForm"
|
||||
label-position="top"
|
||||
:model="form"
|
||||
|
@@ -15,7 +15,9 @@
|
||||
<template #label>
|
||||
<a href="javascript:void(0);" @click="toPage(item[0])">{{ $t('app.' + item[0]) }}</a>
|
||||
</template>
|
||||
{{ map.get(item[0]).toString() }}
|
||||
<span style="word-break: break-all">
|
||||
{{ map.get(item[0]).toString() }}
|
||||
</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-col>
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<el-row v-else v-loading="loading">
|
||||
<el-col :span="22" :offset="1">
|
||||
<el-alert :title="$t('app.updateHelper')" type="warning" :closable="false" />
|
||||
<el-form ref="paramForm" :model="paramModel" label-position="top" :rules="rules">
|
||||
<el-form @submit.prevent ref="paramForm" :model="paramModel" label-position="top" :rules="rules">
|
||||
<div v-for="(p, index) in params" :key="index">
|
||||
<el-form-item :prop="p.key" :label="getLabel(p)">
|
||||
<el-input
|
||||
|
@@ -5,7 +5,14 @@
|
||||
</template>
|
||||
<el-row>
|
||||
<el-col :span="22" :offset="1">
|
||||
<el-form ref="updateRef" :rules="rules" label-position="top" :model="operateReq" v-loading="loading">
|
||||
<el-form
|
||||
@submit.prevent
|
||||
ref="updateRef"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
:model="operateReq"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-form-item :label="$t('app.versioneSelect')" prop="detailId">
|
||||
<el-select v-model="operateReq.detailId">
|
||||
<el-option
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.monitor')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form label-position="top">
|
||||
<el-form label-position="top" @submit.prevent>
|
||||
<el-form-item :label="$t('container.refreshTime')">
|
||||
<el-select v-model="timeInterval" @change="changeTimer">
|
||||
<el-option label="1s" :value="1" />
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.rename')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form ref="newNameRef" v-loading="loading" :model="renameForm" label-position="top">
|
||||
<el-form @submit.prevent ref="newNameRef" v-loading="loading" :model="renameForm" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('container.newName')" :rules="Rules.volumeName" prop="newName">
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.imageDelete')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form :model="deleteForm" label-position="top">
|
||||
<el-form @submit.prevent :model="deleteForm" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('container.tag')" prop="tagName">
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.importImage')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('container.path')" :rules="Rules.requiredSelect" prop="path">
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('database.remoteAccess')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('database.remoteAccess')" :rules="Rules.requiredInput" prop="privilege">
|
||||
|
@@ -3,21 +3,35 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('database.databaseConnInfo')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('database.rootPassword')" :rules="Rules.requiredInput" prop="password">
|
||||
<el-input type="password" show-password clearable v-model="form.password">
|
||||
<template #append>
|
||||
<el-button @click="copy" icon="DocumentCopy"></el-button>
|
||||
<el-button @click="copy(form.password)" icon="DocumentCopy"></el-button>
|
||||
<el-button style="margin-left: 1px" @click="random" icon="RefreshRight"></el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.serviceName')" prop="serviceName">
|
||||
<el-tag>{{ form.serviceName }}</el-tag>
|
||||
<el-button @click="copy(form.serviceName)" icon="DocumentCopy" link></el-button>
|
||||
<span class="input-help">{{ $t('database.serviceNameHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.containerConn')">
|
||||
<el-tag>
|
||||
{{ form.serviceName + ':3306' }}
|
||||
</el-tag>
|
||||
<el-button @click="copy(form.serviceName + ':3306')" icon="DocumentCopy" link></el-button>
|
||||
<span class="input-help">
|
||||
{{ $t('database.containerConnHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.remoteConn')">
|
||||
<el-tag>{{ $t('database.localIP') + ':' + form.port }}</el-tag>
|
||||
<span class="input-help">{{ $t('database.remoteConnHelper2') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
@@ -56,6 +70,7 @@ const dialogVisiable = ref(false);
|
||||
const form = ref<App.DatabaseConnInfo>({
|
||||
password: '',
|
||||
serviceName: '',
|
||||
port: 0,
|
||||
});
|
||||
|
||||
const confirmDialogRef = ref();
|
||||
@@ -73,9 +88,9 @@ const random = async () => {
|
||||
form.value.password = getRandomStr(16);
|
||||
};
|
||||
|
||||
const copy = async () => {
|
||||
const copy = async (value: string) => {
|
||||
let input = document.createElement('input');
|
||||
input.value = form.value.password;
|
||||
input.value = value;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('Copy');
|
||||
|
@@ -3,21 +3,35 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('database.databaseConnInfo')" :back="handleClose" />
|
||||
</template>
|
||||
<el-form v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('database.requirepass')" :rules="Rules.requiredInput" prop="password">
|
||||
<el-input type="password" show-password clearable v-model="form.password">
|
||||
<template #append>
|
||||
<el-button @click="copy" icon="DocumentCopy"></el-button>
|
||||
<el-button @click="copy(form.password)" icon="DocumentCopy"></el-button>
|
||||
<el-button style="margin-left: 1px" @click="random" icon="RefreshRight"></el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.serviceName')" prop="serviceName">
|
||||
<el-tag>{{ form.serviceName }}</el-tag>
|
||||
<el-button @click="copy(form.serviceName)" icon="DocumentCopy" link></el-button>
|
||||
<span class="input-help">{{ $t('database.serviceNameHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.containerConn')">
|
||||
<el-tag>
|
||||
{{ form.serviceName + ':6379' }}
|
||||
</el-tag>
|
||||
<el-button @click="copy(form.serviceName + ':6379')" icon="DocumentCopy" link></el-button>
|
||||
<span class="input-help">
|
||||
{{ $t('database.containerConnHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.remoteConn')">
|
||||
<el-tag>{{ $t('database.localIP') + ':' + form.port }}</el-tag>
|
||||
<span class="input-help">{{ $t('database.remoteConnHelper2') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
@@ -56,6 +70,7 @@ const dialogVisiable = ref(false);
|
||||
const form = ref<App.DatabaseConnInfo>({
|
||||
password: '',
|
||||
serviceName: '',
|
||||
port: 0,
|
||||
});
|
||||
|
||||
const confirmDialogRef = ref();
|
||||
@@ -78,9 +93,9 @@ const random = async () => {
|
||||
form.value.password = getRandomStr(16);
|
||||
};
|
||||
|
||||
const copy = async () => {
|
||||
const copy = async (value: string) => {
|
||||
let input = document.createElement('input');
|
||||
input.value = form.value.password;
|
||||
input.value = value;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('Copy');
|
||||
|
@@ -210,7 +210,10 @@ import { useRouter } from 'vue-router';
|
||||
import RouterButton from '@/components/router-button/index.vue';
|
||||
import { loadBaseInfo, loadCurrentInfo } from '@/api/modules/dashboard';
|
||||
import { getIOOptions, getNetworkOptions } from '@/api/modules/monitor';
|
||||
import { loadUpgradeInfo } from '@/api/modules/setting';
|
||||
import { GlobalStore } from '@/store';
|
||||
const router = useRouter();
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const statuRef = ref();
|
||||
const appRef = ref();
|
||||
@@ -470,7 +473,17 @@ const loadData = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadUpgradeStatus = async () => {
|
||||
const res = await loadUpgradeInfo();
|
||||
if (res.data) {
|
||||
globalStore.hasNewVersion = true;
|
||||
} else {
|
||||
globalStore.hasNewVersion = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadUpgradeStatus();
|
||||
onLoadNetworkOptions();
|
||||
onLoadIOOptions();
|
||||
onLoadBaseInfo(true, 'all');
|
||||
|
@@ -11,7 +11,14 @@
|
||||
</template>
|
||||
<el-row>
|
||||
<el-col :span="22" :offset="1">
|
||||
<el-form ref="fileForm" label-position="top" :model="addForm" :rules="rules" v-loading="loading">
|
||||
<el-form
|
||||
@submit.prevent
|
||||
ref="fileForm"
|
||||
label-position="top"
|
||||
:model="addForm"
|
||||
:rules="rules"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-form-item :label="$t('file.path')" prop="newPath">
|
||||
<el-input v-model="addForm.newPath">
|
||||
<template #prepend><FileList @choose="getPath" :dir="true"></FileList></template>
|
||||
|
@@ -2,83 +2,109 @@
|
||||
<div v-loading="loading" style="position: relative">
|
||||
<FireRouter />
|
||||
<FireStatus
|
||||
v-show="fireName !== '-'"
|
||||
ref="fireStatuRef"
|
||||
@search="search"
|
||||
v-model:loading="loading"
|
||||
v-model:name="fireName"
|
||||
v-model:mask-show="maskShow"
|
||||
v-model:status="fireStatus"
|
||||
/>
|
||||
|
||||
<el-card v-if="fireStatus != 'running' && maskShow" class="mask-prompt">
|
||||
<span>{{ $t('firewall.firewallNotStart') }}</span>
|
||||
</el-card>
|
||||
<div v-if="fireName !== '-'">
|
||||
<el-card v-if="fireStatus != 'running' && maskShow" class="mask-prompt">
|
||||
<span>{{ $t('firewall.firewallNotStart') }}</span>
|
||||
</el-card>
|
||||
|
||||
<LayoutContent :title="$t('firewall.ipRule')" :class="{ mask: fireStatus != 'running' }">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
<el-button type="primary" @click="onOpenDialog('create')">
|
||||
{{ $t('commons.button.create') }} {{ $t('firewall.ipRule') }}
|
||||
</el-button>
|
||||
<el-button @click="onDelete(null)" plain :disabled="selects.length === 0">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<TableSetting @search="search()" />
|
||||
<div class="search-button">
|
||||
<el-input
|
||||
v-model="searchName"
|
||||
clearable
|
||||
@clear="search()"
|
||||
suffix-icon="Search"
|
||||
@keyup.enter="search()"
|
||||
@blur="search()"
|
||||
:placeholder="$t('commons.button.search')"
|
||||
></el-input>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<template #main>
|
||||
<ComplexTable
|
||||
:pagination-config="paginationConfig"
|
||||
v-model:selects="selects"
|
||||
@search="search"
|
||||
:data="data"
|
||||
>
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column :min-width="80" :label="$t('firewall.address')" prop="address">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.address && row.address !== 'Anywhere'">{{ row.address }}</span>
|
||||
<span v-else>{{ $t('firewall.allIP') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :min-width="80" :label="$t('firewall.strategy')" prop="strategy">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.strategy === 'accept'"
|
||||
@click="onChangeStatus(row, 'drop')"
|
||||
link
|
||||
type="success"
|
||||
<LayoutContent :title="$t('firewall.ipRule')" :class="{ mask: fireStatus != 'running' }">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
<el-button type="primary" @click="onOpenDialog('create')">
|
||||
{{ $t('commons.button.create') }} {{ $t('firewall.ipRule') }}
|
||||
</el-button>
|
||||
<el-button @click="onDelete(null)" plain :disabled="selects.length === 0">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<TableSetting @search="search()" />
|
||||
<div class="search-button">
|
||||
<el-input
|
||||
v-model="searchName"
|
||||
clearable
|
||||
@clear="search()"
|
||||
suffix-icon="Search"
|
||||
@keyup.enter="search()"
|
||||
@blur="search()"
|
||||
:placeholder="$t('commons.button.search')"
|
||||
></el-input>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<template #main>
|
||||
<ComplexTable
|
||||
:pagination-config="paginationConfig"
|
||||
v-model:selects="selects"
|
||||
@search="search"
|
||||
:data="data"
|
||||
>
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column :min-width="80" :label="$t('firewall.address')" prop="address">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.address && row.address !== 'Anywhere'">{{ row.address }}</span>
|
||||
<span v-else>{{ $t('firewall.allIP') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :min-width="80" :label="$t('firewall.strategy')" prop="strategy">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.strategy === 'accept'"
|
||||
@click="onChangeStatus(row, 'drop')"
|
||||
link
|
||||
type="success"
|
||||
>
|
||||
{{ $t('firewall.allow') }}
|
||||
</el-button>
|
||||
<el-button v-else link type="danger" @click="onChangeStatus(row, 'accept')">
|
||||
{{ $t('firewall.deny') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<fu-table-operations
|
||||
width="200px"
|
||||
:buttons="buttons"
|
||||
:ellipsis="10"
|
||||
:label="$t('commons.table.operate')"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
</div>
|
||||
<div v-else>
|
||||
<LayoutContent :title="$t('firewall.firewall')" :divider="true">
|
||||
<template #main>
|
||||
<div class="app-warn">
|
||||
<div>
|
||||
<span>{{ $t('firewall.notSupport') }}</span>
|
||||
<el-link
|
||||
style="font-size: 12px; margin-left: 5px"
|
||||
@click="toDoc"
|
||||
icon="Position"
|
||||
type="primary"
|
||||
>
|
||||
{{ $t('firewall.allow') }}
|
||||
</el-button>
|
||||
<el-button v-else link type="danger" @click="onChangeStatus(row, 'accept')">
|
||||
{{ $t('firewall.deny') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<fu-table-operations
|
||||
width="200px"
|
||||
:buttons="buttons"
|
||||
:ellipsis="10"
|
||||
:label="$t('commons.table.operate')"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
{{ $t('firewall.quickJump') }}
|
||||
</el-link>
|
||||
<div>
|
||||
<img src="@/assets/images/no_app.svg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
</div>
|
||||
|
||||
<OperatrDialog @search="search" ref="dialogRef" />
|
||||
</div>
|
||||
@@ -102,6 +128,7 @@ const loading = ref();
|
||||
const activeTag = ref('address');
|
||||
const selects = ref<any>([]);
|
||||
const searchName = ref();
|
||||
const fireName = ref();
|
||||
|
||||
const maskShow = ref(true);
|
||||
const fireStatus = ref('running');
|
||||
@@ -153,6 +180,10 @@ const onOpenDialog = async (
|
||||
dialogRef.value!.acceptParams(params);
|
||||
};
|
||||
|
||||
const toDoc = () => {
|
||||
window.open('https://1panel.cn/docs/user_manual/hosts/firewall/', '_blank');
|
||||
};
|
||||
|
||||
const onChangeStatus = async (row: Host.RuleInfo, status: string) => {
|
||||
let operation =
|
||||
status === 'accept'
|
||||
@@ -244,7 +275,9 @@ const buttons = [
|
||||
];
|
||||
|
||||
onMounted(() => {
|
||||
loading.value = true;
|
||||
fireStatuRef.value.acceptParams();
|
||||
if (fireName.value !== '-') {
|
||||
loading.value = true;
|
||||
fireStatuRef.value.acceptParams();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user