mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 00:09:16 +08:00
feat: 网站创建增加 FTP 选项 (#5076)
This commit is contained in:
parent
5e951f028e
commit
d9f9f9a629
@ -122,7 +122,7 @@ func (b *BaseApi) CreateFtp(c *gin.Context) {
|
||||
}
|
||||
req.Password = string(pass)
|
||||
}
|
||||
if err := ftpService.Create(req); err != nil {
|
||||
if _, err := ftpService.Create(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
@ -76,6 +78,14 @@ func (b *BaseApi) CreateWebsite(c *gin.Context) {
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if len(req.FtpPassword) != 0 {
|
||||
pass, err := base64.StdEncoding.DecodeString(req.FtpPassword)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
req.FtpPassword = string(pass)
|
||||
}
|
||||
err := websiteService.CreateWebsite(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
|
@ -27,6 +27,9 @@ type WebsiteCreate struct {
|
||||
AppID uint `json:"appID"`
|
||||
AppInstallID uint `json:"appInstallID"`
|
||||
|
||||
FtpUser string `json:"ftpUser"`
|
||||
FtpPassword string `json:"ftpPassword"`
|
||||
|
||||
RuntimeID uint `json:"runtimeID"`
|
||||
RuntimeConfig
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ type Website struct {
|
||||
WebsiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
|
||||
RuntimeID uint `gorm:"type:integer" json:"runtimeID"`
|
||||
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
|
||||
FtpID uint `gorm:"type:integer" json:"ftpId"`
|
||||
|
||||
User string `gorm:"type:varchar;" json:"user"`
|
||||
Group string `gorm:"type:varchar;" json:"group"`
|
||||
|
@ -1,6 +1,9 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
@ -16,7 +19,7 @@ type IFtpService interface {
|
||||
LoadBaseInfo() (dto.FtpBaseInfo, error)
|
||||
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||
Operate(operation string) error
|
||||
Create(req dto.FtpCreate) error
|
||||
Create(req dto.FtpCreate) (uint, error)
|
||||
Delete(req dto.BatchDeleteReq) error
|
||||
Update(req dto.FtpUpdate) error
|
||||
Sync() error
|
||||
@ -122,32 +125,39 @@ func (f *FtpService) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FtpService) Create(req dto.FtpCreate) error {
|
||||
func (f *FtpService) Create(req dto.FtpCreate) (uint, error) {
|
||||
if _, err := os.Stat(req.Path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Println(os.MkdirAll(req.Path, os.ModePerm))
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
pass, err := encrypt.StringEncrypt(req.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
userInDB, _ := ftpRepo.Get(hostRepo.WithByUser(req.User))
|
||||
if userInDB.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
return 0, constant.ErrRecordExist
|
||||
}
|
||||
client, err := toolbox.NewFtpClient()
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
if err := client.UserAdd(req.User, req.Password, req.Path); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
var ftp model.Ftp
|
||||
if err := copier.Copy(&ftp, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
return 0, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
ftp.Status = constant.StatusEnable
|
||||
ftp.Password = pass
|
||||
if err := ftpRepo.Create(&ftp); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
return nil
|
||||
return ftp.ID, nil
|
||||
}
|
||||
|
||||
func (f *FtpService) Delete(req dto.BatchDeleteReq) error {
|
||||
|
@ -331,6 +331,14 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(create.FtpUser) != 0 && len(create.FtpPassword) != 0 {
|
||||
itemID, err := NewIFtpService().Create(dto.FtpCreate{User: create.FtpUser, Password: create.FtpPassword, Path: path.Join(global.CONF.System.BaseDir, "1panel/apps/openresty/openresty/www/sites", create.Alias, "index")})
|
||||
if err != nil {
|
||||
global.LOG.Errorf("create ftp for website failed, err: %v", err)
|
||||
}
|
||||
website.FtpID = itemID
|
||||
}
|
||||
|
||||
if err = createWafConfig(website, domains); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -205,9 +205,9 @@ var AddMonitorMenu = &gormigrate.Migration{
|
||||
}
|
||||
|
||||
var AddFtp = &gormigrate.Migration{
|
||||
ID: "20240517-add-ftp",
|
||||
ID: "20240521-add-ftp",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Ftp{}); err != nil {
|
||||
if err := tx.AutoMigrate(&model.Ftp{}, model.Website{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -16,7 +16,8 @@ import (
|
||||
)
|
||||
|
||||
type Ftp struct {
|
||||
DefaultUser string
|
||||
DefaultUser string
|
||||
DefaultGroup string
|
||||
}
|
||||
|
||||
type FtpClient interface {
|
||||
@ -33,7 +34,11 @@ type FtpClient interface {
|
||||
func NewFtpClient() (*Ftp, error) {
|
||||
userItem, err := user.LookupId("1000")
|
||||
if err == nil {
|
||||
return &Ftp{DefaultUser: userItem.Username}, err
|
||||
groupItem, err := user.LookupGroupId(userItem.Gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Ftp{DefaultUser: userItem.Username, DefaultGroup: groupItem.Name}, err
|
||||
}
|
||||
if err.Error() != user.UnknownUserIdError(1000).Error() {
|
||||
return nil, err
|
||||
@ -45,7 +50,7 @@ func NewFtpClient() (*Ftp, error) {
|
||||
if err != nil {
|
||||
return nil, errors.New(stdout2)
|
||||
}
|
||||
return &Ftp{DefaultUser: "1panel"}, nil
|
||||
return &Ftp{DefaultUser: "1panel", DefaultGroup: groupItem.Name}, nil
|
||||
}
|
||||
if err.Error() != user.UnknownGroupIdError("1000").Error() {
|
||||
return nil, err
|
||||
@ -54,11 +59,11 @@ func NewFtpClient() (*Ftp, error) {
|
||||
if err != nil {
|
||||
return nil, errors.New(string(stdout))
|
||||
}
|
||||
stdout2, err := cmd.Execf("useradd -u 1000 -g %s %s", groupItem.Name, userItem.Username)
|
||||
stdout2, err := cmd.Execf("useradd -u 1000 -g 1panel %s", userItem.Username)
|
||||
if err != nil {
|
||||
return nil, errors.New(stdout2)
|
||||
}
|
||||
return &Ftp{DefaultUser: "1panel"}, nil
|
||||
return &Ftp{DefaultUser: "1panel", DefaultGroup: "1panel"}, nil
|
||||
}
|
||||
|
||||
func (f *Ftp) Status() (bool, bool) {
|
||||
@ -87,7 +92,7 @@ func (f *Ftp) UserAdd(username, passwd, path string) error {
|
||||
return errors.New(std)
|
||||
}
|
||||
_ = f.Reload()
|
||||
std2, err := cmd.Execf("chown %s %s", f.DefaultUser, path)
|
||||
std2, err := cmd.Execf("chown -R %s:%s %s", f.DefaultUser, f.DefaultGroup, path)
|
||||
if err != nil {
|
||||
return errors.New(std2)
|
||||
}
|
||||
@ -170,19 +175,19 @@ func (f *Ftp) LoadLogs(user, operation string) ([]FtpLog, error) {
|
||||
logItem := ""
|
||||
if _, err := os.Stat("/etc/pure-ftpd/conf"); err != nil && os.IsNotExist(err) {
|
||||
std, err := cmd.Exec("cat /etc/pure-ftpd/pure-ftpd.conf | grep AltLog | grep clf:")
|
||||
if err != nil {
|
||||
return logs, err
|
||||
logItem = "/var/log/pureftpd.log"
|
||||
if err == nil && !strings.HasPrefix(logItem, "#") {
|
||||
logItem = std
|
||||
}
|
||||
logItem = std
|
||||
} else {
|
||||
if err != nil {
|
||||
return logs, err
|
||||
}
|
||||
std, err := cmd.Exec("cat /etc/pure-ftpd/conf/AltLog")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
logItem = "/var/log/pure-ftpd/transfer.log"
|
||||
if err != nil && !strings.HasPrefix(logItem, "#") {
|
||||
logItem = std
|
||||
}
|
||||
logItem = std
|
||||
}
|
||||
|
||||
logItem = strings.ReplaceAll(logItem, "AltLog", "")
|
||||
|
@ -19029,6 +19029,9 @@ const docTemplate = `{
|
||||
"expireDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"ftpId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -20820,6 +20823,12 @@ const docTemplate = `{
|
||||
"installed"
|
||||
]
|
||||
},
|
||||
"ftpPassword": {
|
||||
"type": "string"
|
||||
},
|
||||
"ftpUser": {
|
||||
"type": "string"
|
||||
},
|
||||
"otherDomains": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -22062,6 +22071,9 @@ const docTemplate = `{
|
||||
"expireDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"ftpId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -19022,6 +19022,9 @@
|
||||
"expireDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"ftpId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -20813,6 +20816,12 @@
|
||||
"installed"
|
||||
]
|
||||
},
|
||||
"ftpPassword": {
|
||||
"type": "string"
|
||||
},
|
||||
"ftpUser": {
|
||||
"type": "string"
|
||||
},
|
||||
"otherDomains": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -22055,6 +22064,9 @@
|
||||
"expireDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"ftpId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -3050,6 +3050,8 @@ definitions:
|
||||
type: boolean
|
||||
expireDate:
|
||||
type: string
|
||||
ftpId:
|
||||
type: integer
|
||||
group:
|
||||
type: string
|
||||
httpConfig:
|
||||
@ -4248,6 +4250,10 @@ definitions:
|
||||
- new
|
||||
- installed
|
||||
type: string
|
||||
ftpPassword:
|
||||
type: string
|
||||
ftpUser:
|
||||
type: string
|
||||
otherDomains:
|
||||
type: string
|
||||
port:
|
||||
@ -5080,6 +5086,8 @@ definitions:
|
||||
type: string
|
||||
expireDate:
|
||||
type: string
|
||||
ftpId:
|
||||
type: integer
|
||||
group:
|
||||
type: string
|
||||
httpConfig:
|
||||
|
@ -64,6 +64,8 @@ export namespace Website {
|
||||
otherDomains: string;
|
||||
proxy: string;
|
||||
proxyType: string;
|
||||
ftpUser: string;
|
||||
ftpPassword: string;
|
||||
}
|
||||
|
||||
export interface WebSiteUpdateReq {
|
||||
|
@ -3,6 +3,8 @@ import { ReqPage, ResPage } from '../interface';
|
||||
import { Website } from '../interface/website';
|
||||
import { File } from '../interface/file';
|
||||
import { TimeoutEnum } from '@/enums/http-enum';
|
||||
import { deepCopy } from '@/utils/util';
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
export const SearchWebsites = (req: Website.WebSiteSearch) => {
|
||||
return http.post<ResPage<Website.WebsiteDTO>>(`/websites/search`, req);
|
||||
@ -13,7 +15,11 @@ export const ListWebsites = () => {
|
||||
};
|
||||
|
||||
export const CreateWebsite = (req: Website.WebSiteCreateReq) => {
|
||||
return http.post<any>(`/websites`, req);
|
||||
let request = deepCopy(req) as Website.WebSiteCreateReq;
|
||||
if (request.ftpPassword) {
|
||||
request.ftpPassword = Base64.encode(request.ftpPassword);
|
||||
}
|
||||
return http.post<any>(`/websites`, request);
|
||||
};
|
||||
|
||||
export const OpWebsite = (req: Website.WebSiteOp) => {
|
||||
|
@ -181,6 +181,19 @@ const checkSimpleName = (rule: any, value: any, callback: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
const checkSimplePassword = (rule: any, value: any, callback: any) => {
|
||||
if (value === '' || typeof value === 'undefined' || value == null) {
|
||||
callback(new Error(i18n.global.t('commons.rule.simplePassword')));
|
||||
} else {
|
||||
const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9_]{5,29}$/;
|
||||
if (!reg.test(value) && value !== '') {
|
||||
callback(new Error(i18n.global.t('commons.rule.simplePassword')));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const checkDBName = (rule: any, value: any, callback: any) => {
|
||||
if (value === '' || typeof value === 'undefined' || value == null) {
|
||||
callback(new Error(i18n.global.t('commons.rule.dbName')));
|
||||
@ -535,6 +548,7 @@ interface CommonRule {
|
||||
name: FormItemRule;
|
||||
userName: FormItemRule;
|
||||
simpleName: FormItemRule;
|
||||
simplePassword: FormItemRule;
|
||||
dbName: FormItemRule;
|
||||
imageName: FormItemRule;
|
||||
volumeName: FormItemRule;
|
||||
@ -601,6 +615,11 @@ export const Rules: CommonRule = {
|
||||
validator: checkSimpleName,
|
||||
trigger: 'blur',
|
||||
},
|
||||
simplePassword: {
|
||||
required: true,
|
||||
validator: checkSimplePassword,
|
||||
trigger: 'blur',
|
||||
},
|
||||
dbName: {
|
||||
required: true,
|
||||
validator: checkDBName,
|
||||
|
@ -170,7 +170,8 @@ const message = {
|
||||
commonName:
|
||||
'Supports non-special characters starting with English, Chinese, numbers, .- and _, length 1-128',
|
||||
userName: 'Support English, Chinese, numbers and _ length 3-30',
|
||||
simpleName: 'Supports non-underscore starting, English, numbers, _, length 1-30',
|
||||
simpleName: 'Supports non-underscore starting, English, numbers, _, length 3-30',
|
||||
simplePassword: 'Supports non-underscore starting, English, numbers, _, length 6-30',
|
||||
dbName: 'Supports non-special character starting, including English, Chinese, numbers, .-_, with a length of 1-64',
|
||||
imageName: 'Support English, numbers, :/.-_, length 1-150',
|
||||
volumeName: 'Support English, numbers, .-_, length 2-30',
|
||||
@ -1736,6 +1737,10 @@ const message = {
|
||||
zipFormat: '.tar.gz compressed package structure: test.tar.gz compressed package must contain {0} file',
|
||||
proxy: 'Reverse Proxy',
|
||||
alias: 'Path Name',
|
||||
ftpUser: 'FTP Account',
|
||||
ftpPassword: 'FTP Password',
|
||||
ftpHelper:
|
||||
'Create an FTP account corresponding to the site while creating the site, and the FTP directory points to the directory where the site is located.',
|
||||
remark: 'Remark',
|
||||
group: 'Group',
|
||||
groupSetting: 'Group Management',
|
||||
|
@ -170,7 +170,8 @@ const message = {
|
||||
illegalInput: '輸入框中存在不合法字符',
|
||||
commonName: '支持非特殊字元開頭,英文、中文、數字、.-和_,長度1-128',
|
||||
userName: '支持英文、中文、數字和_,長度3-30',
|
||||
simpleName: '支持非底線開頭,英文、數字、_,長度1-30',
|
||||
simpleName: '支持非底線開頭,英文、數字、_,長度3-30',
|
||||
simplePassword: '支持非底線開頭,英文、數字、_,長度6-30',
|
||||
dbName: '支持非特殊字符開頭,英文、中文、數字、.-_,長度1-64',
|
||||
imageName: '支持英文、數字、:/.-_,長度1-150',
|
||||
volumeName: '支持英文、數字、.-和_,長度2-30',
|
||||
@ -1617,6 +1618,9 @@ const message = {
|
||||
zipFormat: '.tar.gz 壓縮包結構:test.tar.gz 壓縮包內,必需包含 {0} 文件',
|
||||
proxy: '反向代理',
|
||||
alias: '代號',
|
||||
ftpUser: 'FTP 帳號',
|
||||
ftpPassword: 'FTP 密碼',
|
||||
ftpHelper: '建立站點的同時,為站點建立一個對應 FTP 帳戶,並且 FTP 目錄指向站點所在目錄。',
|
||||
remark: '備註',
|
||||
group: '分組',
|
||||
groupSetting: '分組管理',
|
||||
|
@ -171,6 +171,7 @@ const message = {
|
||||
commonName: '支持非特殊字符开头,英文、中文、数字、.-和_,长度1-128',
|
||||
userName: '支持英文、中文、数字和_,长度3-30',
|
||||
simpleName: '支持非下划线开头,英文、数字、_,长度3-30',
|
||||
simplePassword: '支持非下划线开头,英文、数字、_,长度6-30',
|
||||
dbName: '支持非特殊字符开头,英文、中文、数字、.-_,长度1-64',
|
||||
imageName: '支持英文、数字、:/.-_,长度1-150',
|
||||
volumeName: '支持英文、数字、.-和_,长度2-30',
|
||||
@ -1617,6 +1618,10 @@ const message = {
|
||||
zipFormat: '.tar.gz 压缩包结构:test.tar.gz 压缩包内,必需包含 {0} 文件',
|
||||
proxy: '反向代理',
|
||||
alias: '代号',
|
||||
enableFtp: '创建 FTP',
|
||||
ftpUser: 'FTP 账号',
|
||||
ftpPassword: 'FTP 密码',
|
||||
ftpHelper: '创建站点的同时,为站点创建一个对应 FTP 帐户,并且 FTP 目录指向站点所在目录。',
|
||||
remark: '备注',
|
||||
group: '分组',
|
||||
groupSetting: '分组管理',
|
||||
|
@ -154,7 +154,7 @@
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.username === ''">-</span>
|
||||
<div class="flex items-center" v-if="row.password && row.username">
|
||||
<div class="flex items-center flex-wrap" v-if="row.password && row.username">
|
||||
<div class="star-center" v-if="!row.showPassword">
|
||||
<span>**********</span>
|
||||
</div>
|
||||
|
@ -23,7 +23,7 @@
|
||||
<el-table-column :label="$t('commons.login.username')" prop="username" />
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center flex-wrap">
|
||||
<div class="star-center">
|
||||
<span v-if="!row.showPassword">**********</span>
|
||||
</div>
|
||||
|
@ -131,7 +131,7 @@
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.username === '' || row.password === ''">-</span>
|
||||
<div class="flex items-center" v-else>
|
||||
<div class="flex items-center flex-wrap" v-else>
|
||||
<div class="star-center" v-if="!row.showPassword">
|
||||
<span>**********</span>
|
||||
</div>
|
||||
|
@ -23,7 +23,7 @@
|
||||
<el-table-column :label="$t('commons.login.username')" prop="username" />
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center flex-wrap">
|
||||
<div class="star-center">
|
||||
<span v-if="!row.showPassword">**********</span>
|
||||
</div>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<el-table-column show-overflow-tooltip :label="$t('database.address')" prop="address" />
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center flex-wrap">
|
||||
<div class="star-center">
|
||||
<span v-if="!row.showPassword">**********</span>
|
||||
</div>
|
||||
|
@ -63,7 +63,7 @@
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.password.length === 0">-</div>
|
||||
<div v-else class="flex items-center">
|
||||
<div v-else class="flex items-center flex-wrap">
|
||||
<div class="star-center" v-if="!row.showPassword">
|
||||
<span>**********</span>
|
||||
</div>
|
||||
@ -117,7 +117,11 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.root')" :min-width="120" prop="path" show-overflow-tooltip />
|
||||
<el-table-column :label="$t('file.root')" :min-width="120" prop="path">
|
||||
<template #default="{ row }">
|
||||
<Tooltip @click="toFolder(row.path)" :text="row.path" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('commons.table.description')"
|
||||
:min-width="80"
|
||||
@ -126,12 +130,13 @@
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('commons.table.createdAt')"
|
||||
show-overflow-tooltip
|
||||
:formatter="dateFormat"
|
||||
:min-width="80"
|
||||
prop="createdAt"
|
||||
/>
|
||||
<fu-table-operations
|
||||
width="240px"
|
||||
width="200px"
|
||||
:buttons="buttons"
|
||||
:ellipsis="10"
|
||||
:label="$t('commons.table.operate')"
|
||||
@ -175,6 +180,7 @@ import { deleteFtp, searchFtp, updateFtp, syncFtp, operateFtp, getFtpBase } from
|
||||
import OperateDialog from '@/views/toolbox/ftp/operate/index.vue';
|
||||
import LogDialog from '@/views/toolbox/ftp/log/index.vue';
|
||||
import { Toolbox } from '@/api/interface/toolbox';
|
||||
import router from '@/routers';
|
||||
|
||||
const loading = ref();
|
||||
const selects = ref<any>([]);
|
||||
@ -232,6 +238,10 @@ const toDoc = () => {
|
||||
window.open('https://1panel.cn/docs/user_manual/toolbox/ftp/', '_blank', 'noopener,noreferrer');
|
||||
};
|
||||
|
||||
const toFolder = (folder: string) => {
|
||||
router.push({ path: '/hosts/files', query: { path: folder } });
|
||||
};
|
||||
|
||||
const onOperate = async (operation: string) => {
|
||||
let msg = operation === 'enable' || operation === 'disable' ? 'ssh.' : 'commons.button.';
|
||||
ElMessageBox.confirm(i18n.global.t('toolbox.ftp.operation', [i18n.global.t(msg + operation)]), 'FTP', {
|
||||
@ -348,7 +358,7 @@ const buttons = [
|
||||
return row.status === 'deleted';
|
||||
},
|
||||
click: (row: Toolbox.FtpInfo) => {
|
||||
dialogLogRef.value!.acceptParams({ user: row.user });
|
||||
dialogLogRef.value!.acceptParams({ user: row.user, path: row.path });
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -15,9 +15,8 @@
|
||||
<el-option value="GET" :label="$t('file.download')" />
|
||||
</el-select>
|
||||
|
||||
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search">
|
||||
<el-table-column label="ip" prop="ip" />
|
||||
<el-table-column :label="$t('commons.login.username')" prop="user" />
|
||||
<ComplexTable class="mt-2" :pagination-config="paginationConfig" :data="data" @search="search">
|
||||
<el-table-column label="ip" prop="ip" show-overflow-tooltip />
|
||||
<el-table-column :label="$t('commons.table.status')" show-overflow-tooltip prop="status">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.status === '200'">{{ $t('commons.status.success') }}</el-tag>
|
||||
@ -26,12 +25,12 @@
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.operate')" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ loadFileName(row.operation) }}
|
||||
{{ loadOperation(row.operation) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.file')" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ loadOperation(row.operation) }}
|
||||
{{ loadFileName(row.operation) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.size')" show-overflow-tooltip prop="size">
|
||||
@ -66,14 +65,18 @@ const paginationConfig = reactive({
|
||||
});
|
||||
const data = ref();
|
||||
|
||||
const itemPath = ref();
|
||||
interface DialogProps {
|
||||
user: string;
|
||||
path: string;
|
||||
}
|
||||
const loading = ref();
|
||||
const drawerVisible = ref(false);
|
||||
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
paginationConfig.user = params.user;
|
||||
paginationConfig.operation = '';
|
||||
itemPath.value = params.path;
|
||||
search();
|
||||
drawerVisible.value = true;
|
||||
};
|
||||
@ -101,7 +104,7 @@ const search = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const loadFileName = (operation: string) => {
|
||||
const loadOperation = (operation: string) => {
|
||||
if (operation.startsWith('"PUT')) {
|
||||
return i18n.global.t('file.upload');
|
||||
}
|
||||
@ -109,8 +112,12 @@ const loadFileName = (operation: string) => {
|
||||
return i18n.global.t('file.download');
|
||||
}
|
||||
};
|
||||
const loadOperation = (operation: string) => {
|
||||
return operation.replaceAll('"', '').replaceAll('PUT', '').replaceAll('GET', '');
|
||||
const loadFileName = (operation: string) => {
|
||||
return operation
|
||||
.replaceAll('"', '')
|
||||
.replaceAll('PUT', '')
|
||||
.replaceAll('GET', '')
|
||||
.replaceAll(itemPath.value + '/', '');
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
|
@ -14,7 +14,7 @@
|
||||
:back="handleClose"
|
||||
/>
|
||||
</template>
|
||||
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules">
|
||||
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules" v-loading="loading">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('commons.login.username')" prop="user">
|
||||
@ -25,7 +25,11 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.login.password')" prop="password">
|
||||
<el-input clearable v-model="dialogData.rowData!.password" />
|
||||
<el-input type="password" clearable v-model="dialogData.rowData!.password" show-password>
|
||||
<template #append>
|
||||
<el-button @click="random">{{ $t('commons.button.random') }}</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('file.root')" prop="path">
|
||||
<el-input v-model="dialogData.rowData!.path">
|
||||
@ -43,7 +47,7 @@
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit(formRef)">
|
||||
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
@ -61,6 +65,7 @@ import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { Toolbox } from '@/api/interface/toolbox';
|
||||
import { createFtp, updateFtp } from '@/api/modules/toolbox';
|
||||
import { getRandomStr } from '@/utils/util';
|
||||
|
||||
interface DialogProps {
|
||||
title: string;
|
||||
@ -81,14 +86,18 @@ const acceptParams = (params: DialogProps): void => {
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const random = async () => {
|
||||
dialogData.value.rowData.password = getRandomStr(16);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
user: [Rules.requiredInput, Rules.noSpace],
|
||||
password: [Rules.requiredInput],
|
||||
path: [Rules.requiredInput],
|
||||
user: [Rules.simpleName],
|
||||
password: [Rules.simplePassword],
|
||||
path: [Rules.requiredInput, Rules.noSpace],
|
||||
});
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
|
@ -300,6 +300,33 @@
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="enableFtp" v-if="website.type === 'static' || website.type === 'runtime'">
|
||||
<el-checkbox
|
||||
@change="random"
|
||||
v-model="website.enableFtp"
|
||||
:label="$t('website.enableFtp')"
|
||||
size="large"
|
||||
/>
|
||||
<span class="input-help">{{ $t('website.ftpHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-row :gutter="20" v-if="website.enableFtp">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="ftpUser" :label="$t('website.ftpUser')">
|
||||
<el-input v-model="website.ftpUser" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="ftpPassword" :label="$t('website.ftpPassword')">
|
||||
<el-input type="password" clearable v-model="website.ftpPassword" show-password>
|
||||
<template #append>
|
||||
<el-button @click="random">{{ $t('commons.button.random') }}</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item
|
||||
v-if="website.type === 'proxy'"
|
||||
:label="$t('website.proxyAddress')"
|
||||
@ -316,7 +343,7 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.remark')" prop="remark">
|
||||
<el-input v-model="website.remark"></el-input>
|
||||
<el-input type="textarea" :rows="3" clearable v-model="website.remark" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
@ -354,6 +381,7 @@ import { GetGroupList } from '@/api/modules/group';
|
||||
import { Group } from '@/api/interface/group';
|
||||
import { SearchRuntimes } from '@/api/modules/runtime';
|
||||
import { Runtime } from '@/api/interface/runtime';
|
||||
import { getRandomStr } from '@/utils/util';
|
||||
|
||||
const websiteForm = ref<FormInstance>();
|
||||
const website = ref({
|
||||
@ -383,6 +411,9 @@ const website = ref({
|
||||
allowPort: false,
|
||||
},
|
||||
IPV6: false,
|
||||
enableFtp: false,
|
||||
ftpUser: '',
|
||||
ftpPassword: '',
|
||||
proxyType: 'tcp',
|
||||
port: 9000,
|
||||
proxyProtocol: 'http://',
|
||||
@ -406,6 +437,8 @@ const rules = ref<any>({
|
||||
memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],
|
||||
containerName: [Rules.containerName],
|
||||
},
|
||||
ftpUser: [Rules.simpleName],
|
||||
ftpPassword: [Rules.simplePassword],
|
||||
proxyType: [Rules.requiredSelect],
|
||||
port: [Rules.port],
|
||||
runtimeType: [Rules.requiredInput],
|
||||
@ -443,6 +476,10 @@ const handleClose = () => {
|
||||
em('close', false);
|
||||
};
|
||||
|
||||
const random = async () => {
|
||||
website.value.ftpPassword = getRandomStr(16);
|
||||
};
|
||||
|
||||
const changeType = (type: string) => {
|
||||
switch (type) {
|
||||
case 'deployment':
|
||||
@ -614,6 +651,10 @@ const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (website.value.type === 'proxy') {
|
||||
website.value.proxy = website.value.proxyProtocol + website.value.proxyAddress;
|
||||
}
|
||||
if (!website.value.enableFtp) {
|
||||
website.value.ftpUser = '';
|
||||
website.value.ftpPassword = '';
|
||||
}
|
||||
CreateWebsite(website.value)
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||
|
Loading…
x
Reference in New Issue
Block a user