mirror of
https://github.com/acepanel/panel.git
synced 2026-02-03 22:22:45 +08:00
feat: 同步修改
This commit is contained in:
@@ -3,6 +3,7 @@ package mysql
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
@@ -80,11 +81,16 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := shell.Execf(`mysqladmin -u root -p "%s" extended-status`, rootPassword)
|
||||
if err = os.Setenv("MYSQL_PWD", rootPassword); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "设置环境变量失败")
|
||||
return
|
||||
}
|
||||
raw, err := shell.Execf(`mysqladmin -u root extended-status`)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取负载失败")
|
||||
return
|
||||
}
|
||||
_ = os.Unsetenv("MYSQL_PWD")
|
||||
|
||||
var load []map[string]string
|
||||
expressions := []struct {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
@@ -61,59 +60,32 @@ func (r *safeRepo) UpdateSSH(port uint, status bool) error {
|
||||
}
|
||||
|
||||
func (r *safeRepo) GetPingStatus() (bool, error) {
|
||||
if os.IsRHEL() {
|
||||
out, err := shell.Execf(`firewall-cmd --list-all`)
|
||||
if err != nil {
|
||||
return true, errors.New(out)
|
||||
}
|
||||
|
||||
if !strings.Contains(out, `rule protocol value="icmp" drop`) {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
} else {
|
||||
config, err := io.Read("/etc/ufw/before.rules")
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
if strings.Contains(config, "-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT") {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
out, err := shell.Execf(`firewall-cmd --list-all`)
|
||||
if err != nil {
|
||||
return true, errors.New(out)
|
||||
}
|
||||
|
||||
if !strings.Contains(out, `rule protocol value="icmp" drop`) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *safeRepo) UpdatePingStatus(status bool) error {
|
||||
var out string
|
||||
var err error
|
||||
if os.IsRHEL() {
|
||||
if status {
|
||||
out, err = shell.Execf(`firewall-cmd --permanent --remove-rich-rule='rule protocol value=icmp drop'`)
|
||||
} else {
|
||||
out, err = shell.Execf(`firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop'`)
|
||||
}
|
||||
if status {
|
||||
_, err = shell.Execf(`firewall-cmd --permanent --remove-rich-rule='rule protocol value=icmp drop'`)
|
||||
} else {
|
||||
if status {
|
||||
out, err = shell.Execf(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/g' /etc/ufw/before.rules`)
|
||||
} else {
|
||||
out, err = shell.Execf(`sed -i 's/-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT/-A ufw-before-input -p icmp --icmp-type echo-request -j DROP/g' /etc/ufw/before.rules`)
|
||||
}
|
||||
_, err = shell.Execf(`firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop'`)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.New(out)
|
||||
}
|
||||
|
||||
if os.IsRHEL() {
|
||||
out, err = shell.Execf(`firewall-cmd --reload`)
|
||||
} else {
|
||||
out, err = shell.Execf(`ufw reload`)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = shell.Execf(`firewall-cmd --reload`)
|
||||
if err != nil {
|
||||
return errors.New(out)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -44,10 +44,13 @@ func (r *settingRepo) Get(key biz.SettingKey, defaultValue ...string) (string, e
|
||||
|
||||
func (r *settingRepo) Set(key biz.SettingKey, value string) error {
|
||||
setting := new(biz.Setting)
|
||||
if err := app.Orm.Where("key = ?", key).FirstOrInit(setting).Error; err != nil {
|
||||
return err
|
||||
if err := app.Orm.Where("key = ?", key).First(setting).Error; err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
setting.Key = key
|
||||
setting.Value = value
|
||||
return app.Orm.Save(setting).Error
|
||||
}
|
||||
@@ -278,6 +281,7 @@ func (r *settingRepo) UpdatePanel(version, url, checksum string) error {
|
||||
|
||||
_, _ = shell.Execf("systemctl daemon-reload")
|
||||
_ = io.Remove("/tmp/panel-storage.zip")
|
||||
_ = io.Remove(filepath.Join(app.Root, "panel/config.example.yml"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func (r *sshRepo) UpdateInfo(req *request.SSHUpdateInfo) error {
|
||||
if err := r.settingRepo.Set(biz.SettingKeySshHost, req.Host); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.settingRepo.Set(biz.SettingKeySshPort, req.Port); err != nil {
|
||||
if err := r.settingRepo.Set(biz.SettingKeySshPort, cast.ToString(req.Port)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.settingRepo.Set(biz.SettingKeySshUser, req.User); err != nil {
|
||||
|
||||
@@ -178,7 +178,7 @@ func (r *websiteRepo) Create(req *request.WebsiteCreate) (*biz.Website, error) {
|
||||
Name: req.Name,
|
||||
Status: true,
|
||||
Path: req.Path,
|
||||
PHP: cast.ToInt(req.PHP),
|
||||
PHP: req.PHP,
|
||||
SSL: false,
|
||||
}
|
||||
if err := app.Orm.Create(w).Error; err != nil {
|
||||
@@ -250,7 +250,7 @@ server
|
||||
# ssl标记位结束
|
||||
|
||||
# php标记位开始
|
||||
include enable-php-%s.conf;
|
||||
include enable-php-%d.conf;
|
||||
# php标记位结束
|
||||
|
||||
# 错误页配置,可自行设置
|
||||
|
||||
@@ -2,7 +2,7 @@ package request
|
||||
|
||||
type SSHUpdateInfo struct {
|
||||
Host string `json:"host" form:"host"`
|
||||
Port string `json:"port" form:"port"`
|
||||
Port int `json:"port" form:"port"`
|
||||
User string `json:"user" form:"user"`
|
||||
Password string `json:"password" form:"password"`
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ type WebsiteCreate struct {
|
||||
Domains []string `form:"domains" json:"domains"`
|
||||
Ports []uint `form:"ports" json:"ports"`
|
||||
Path string `form:"path" json:"path"`
|
||||
PHP string `form:"php" json:"php"`
|
||||
PHP int `form:"php" json:"php"`
|
||||
DB bool `form:"db" json:"db"`
|
||||
DBType string `form:"db_type" json:"db_type"`
|
||||
DBName string `form:"db_name" json:"db_name"`
|
||||
|
||||
@@ -166,48 +166,48 @@ func Http(r chi.Router) {
|
||||
container := service.NewContainerService()
|
||||
r.Get("/", container.List)
|
||||
r.Get("/search", container.Search)
|
||||
r.Post("/create", container.Create)
|
||||
r.Post("/remove", container.Remove)
|
||||
r.Post("/start", container.Start)
|
||||
r.Post("/stop", container.Stop)
|
||||
r.Post("/restart", container.Restart)
|
||||
r.Post("/pause", container.Pause)
|
||||
r.Post("/unpause", container.Unpause)
|
||||
r.Get("/inspect", container.Inspect)
|
||||
r.Post("/kill", container.Kill)
|
||||
r.Post("/rename", container.Rename)
|
||||
r.Get("/stats", container.Stats)
|
||||
r.Get("/exist", container.Exist)
|
||||
r.Get("/logs", container.Logs)
|
||||
r.Post("/", container.Create)
|
||||
r.Delete("/{id}", container.Remove)
|
||||
r.Post("/{id}/start", container.Start)
|
||||
r.Post("/{id}/stop", container.Stop)
|
||||
r.Post("/{id}/restart", container.Restart)
|
||||
r.Post("/{id}/pause", container.Pause)
|
||||
r.Post("/{id}/unpause", container.Unpause)
|
||||
r.Get("/{id}/inspect", container.Inspect)
|
||||
r.Post("/{id}/kill", container.Kill)
|
||||
r.Post("/{id}/rename", container.Rename)
|
||||
r.Get("/{id}/stats", container.Stats)
|
||||
r.Get("/{id}/exist", container.Exist)
|
||||
r.Get("/{id}/logs", container.Logs)
|
||||
r.Post("/prune", container.Prune)
|
||||
})
|
||||
r.Route("/network", func(r chi.Router) {
|
||||
containerNetwork := service.NewContainerNetworkService()
|
||||
r.Get("/list", containerNetwork.List)
|
||||
r.Post("/create", containerNetwork.Create)
|
||||
r.Post("/remove", containerNetwork.Remove)
|
||||
r.Get("/exist", containerNetwork.Exist)
|
||||
r.Get("/inspect", containerNetwork.Inspect)
|
||||
r.Post("/connect", containerNetwork.Connect)
|
||||
r.Post("/disconnect", containerNetwork.Disconnect)
|
||||
r.Get("/", containerNetwork.List)
|
||||
r.Post("/", containerNetwork.Create)
|
||||
r.Delete("/{id}", containerNetwork.Remove)
|
||||
r.Get("/{id}/exist", containerNetwork.Exist)
|
||||
r.Get("/{id}/inspect", containerNetwork.Inspect)
|
||||
r.Post("/{network}/connect", containerNetwork.Connect)
|
||||
r.Post("/{network}/disconnect", containerNetwork.Disconnect)
|
||||
r.Post("/prune", containerNetwork.Prune)
|
||||
})
|
||||
r.Route("/image", func(r chi.Router) {
|
||||
containerImage := service.NewContainerImageService()
|
||||
r.Get("/list", containerImage.List)
|
||||
r.Get("/exist", containerImage.Exist)
|
||||
r.Post("/pull", containerImage.Pull)
|
||||
r.Post("/remove", containerImage.Remove)
|
||||
r.Get("/inspect", containerImage.Inspect)
|
||||
r.Get("/", containerImage.List)
|
||||
r.Get("/{id}/exist", containerImage.Exist)
|
||||
r.Post("/", containerImage.Pull)
|
||||
r.Delete("/{id}", containerImage.Remove)
|
||||
r.Get("/{id}", containerImage.Inspect)
|
||||
r.Post("/prune", containerImage.Prune)
|
||||
})
|
||||
r.Route("/volume", func(r chi.Router) {
|
||||
containerVolume := service.NewContainerVolumeService()
|
||||
r.Get("/list", containerVolume.List)
|
||||
r.Post("/create", containerVolume.Create)
|
||||
r.Get("/exist", containerVolume.Exist)
|
||||
r.Post("/remove", containerVolume.Remove)
|
||||
r.Get("/inspect", containerVolume.Inspect)
|
||||
r.Get("/", containerVolume.List)
|
||||
r.Post("/", containerVolume.Create)
|
||||
r.Get("/{id}/exist", containerVolume.Exist)
|
||||
r.Delete("/{id}", containerVolume.Remove)
|
||||
r.Get("/{id}", containerVolume.Inspect)
|
||||
r.Post("/prune", containerVolume.Prune)
|
||||
})
|
||||
})
|
||||
@@ -219,7 +219,7 @@ func Http(r chi.Router) {
|
||||
r.Get("/content", file.Content)
|
||||
r.Post("/save", file.Save)
|
||||
r.Post("/delete", file.Delete)
|
||||
r.Post("/upload", file.Upload)
|
||||
r.Post("/upload", file.Upload) // TODO fix
|
||||
r.Post("/move", file.Move)
|
||||
r.Post("/copy", file.Copy)
|
||||
r.Get("/download", file.Download)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-rat/utils/env"
|
||||
"github.com/go-rat/utils/hash"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/gookit/color"
|
||||
@@ -61,30 +60,11 @@ func (s *CliService) Update(ctx context.Context, cmd *cli.Command) error {
|
||||
return fmt.Errorf("获取最新版本失败:%v", err)
|
||||
}
|
||||
|
||||
// TODO 需要修改接口直接把arch传过去
|
||||
var ver, url, checksum string
|
||||
if env.IsX86() {
|
||||
for _, v := range panel.Downloads {
|
||||
if v.Arch == "amd64" {
|
||||
ver = panel.Version
|
||||
url = v.URL
|
||||
checksum = v.Checksum
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if env.IsArm() {
|
||||
for _, v := range panel.Downloads {
|
||||
if v.Arch == "arm64" {
|
||||
ver = panel.Version
|
||||
url = v.URL
|
||||
checksum = v.Checksum
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
return errors.New("不支持的架构")
|
||||
download := str.FirstElement(panel.Downloads)
|
||||
if download == nil {
|
||||
return fmt.Errorf("下载地址为空")
|
||||
}
|
||||
ver, url, checksum := panel.Version, download.URL, download.Checksum
|
||||
|
||||
return s.setting.UpdatePanel(ver, url, checksum)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/go-rat/utils/env"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
@@ -17,6 +16,7 @@ import (
|
||||
"github.com/TheTNB/panel/pkg/api"
|
||||
"github.com/TheTNB/panel/pkg/db"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
@@ -285,31 +285,12 @@ func (s *InfoService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO 需要修改接口直接把arch传过去
|
||||
var ver, url, checksum string
|
||||
if env.IsX86() {
|
||||
for _, v := range panel.Downloads {
|
||||
if v.Arch == "amd64" {
|
||||
ver = panel.Version
|
||||
url = v.URL
|
||||
checksum = v.Checksum
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if env.IsArm() {
|
||||
for _, v := range panel.Downloads {
|
||||
if v.Arch == "arm64" {
|
||||
ver = panel.Version
|
||||
url = v.URL
|
||||
checksum = v.Checksum
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
Error(w, http.StatusInternalServerError, "不支持的架构")
|
||||
download := str.FirstElement(panel.Downloads)
|
||||
if download == nil {
|
||||
Error(w, http.StatusInternalServerError, "获取下载链接失败")
|
||||
return
|
||||
}
|
||||
ver, url, checksum := panel.Version, download.URL, download.Checksum
|
||||
|
||||
types.Status = types.StatusUpgrade
|
||||
if err = s.settingRepo.UpdatePanel(ver, url, checksum); err != nil {
|
||||
|
||||
@@ -59,9 +59,6 @@ func (s *SSHService) Session(w http.ResponseWriter, r *http.Request) {
|
||||
upGrader := websocket.Upgrader{
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
ws, err := upGrader.Upgrade(w, r, nil)
|
||||
|
||||
@@ -2,20 +2,25 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/go-rat/utils/env"
|
||||
)
|
||||
|
||||
type VersionDownload struct {
|
||||
URL string `json:"url"`
|
||||
Arch string `json:"arch"`
|
||||
Checksum string `json:"checksum"`
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
Downloads []struct {
|
||||
URL string `json:"url"`
|
||||
Arch string `json:"arch"`
|
||||
Checksum string `json:"checksum"`
|
||||
} `json:"downloads"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
Downloads []VersionDownload `json:"downloads"`
|
||||
}
|
||||
|
||||
type Versions []Version
|
||||
@@ -35,6 +40,14 @@ func (r *API) LatestVersion() (*Version, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
arch := "amd64"
|
||||
if env.IsArm() {
|
||||
arch = "arm64"
|
||||
}
|
||||
slices.DeleteFunc(version.Downloads, func(item VersionDownload) bool {
|
||||
return item.Arch != arch
|
||||
})
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# storage
|
||||
|
||||
storage 目录存放应用运行时产生的文件,如上传的文件、缓存文件等。
|
||||
@@ -9,18 +9,18 @@ export default {
|
||||
dnsProviders: (): Promise<AxiosResponse<any>> => request.get('/cert/dnsProviders'),
|
||||
// 证书算法列表
|
||||
algorithms: (): Promise<AxiosResponse<any>> => request.get('/cert/algorithms'),
|
||||
// ACME 用户列表
|
||||
users: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/cert/users', { params: { page, limit } }),
|
||||
// ACME 用户详情
|
||||
userInfo: (id: number): Promise<AxiosResponse<any>> => request.get(`/cert/users/${id}`),
|
||||
// ACME 用户添加
|
||||
userAdd: (data: any): Promise<AxiosResponse<any>> => request.post('/cert/users', data),
|
||||
// ACME 用户更新
|
||||
userUpdate: (id: number, data: any): Promise<AxiosResponse<any>> =>
|
||||
request.put(`/cert/users/${id}`, data),
|
||||
// ACME 用户删除
|
||||
userDelete: (id: number): Promise<AxiosResponse<any>> => request.delete(`/cert/users/${id}`),
|
||||
// ACME 账号列表
|
||||
accounts: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/cert/account', { params: { page, limit } }),
|
||||
// ACME 账号详情
|
||||
accountInfo: (id: number): Promise<AxiosResponse<any>> => request.get(`/cert/account/${id}`),
|
||||
// ACME 账号添加
|
||||
accountAdd: (data: any): Promise<AxiosResponse<any>> => request.post('/cert/account', data),
|
||||
// ACME 账号更新
|
||||
accountUpdate: (id: number, data: any): Promise<AxiosResponse<any>> =>
|
||||
request.put(`/cert/account/${id}`, data),
|
||||
// ACME 账号删除
|
||||
accountDelete: (id: number): Promise<AxiosResponse<any>> => request.delete(`/cert/account/${id}`),
|
||||
// DNS 记录列表
|
||||
dns: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/cert/dns', { params: { page, limit } }),
|
||||
@@ -35,23 +35,26 @@ export default {
|
||||
dnsDelete: (id: number): Promise<AxiosResponse<any>> => request.delete(`/cert/dns/${id}`),
|
||||
// 证书列表
|
||||
certs: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/cert/certs', { params: { page, limit } }),
|
||||
request.get('/cert/cert', { params: { page, limit } }),
|
||||
// 证书详情
|
||||
certInfo: (id: number): Promise<AxiosResponse<any>> => request.get(`/cert/certs/${id}`),
|
||||
certInfo: (id: number): Promise<AxiosResponse<any>> => request.get(`/cert/cert/${id}`),
|
||||
// 证书添加
|
||||
certAdd: (data: any): Promise<AxiosResponse<any>> => request.post('/cert/certs', data),
|
||||
certAdd: (data: any): Promise<AxiosResponse<any>> => request.post('/cert/cert', data),
|
||||
// 证书更新
|
||||
certUpdate: (id: number, data: any): Promise<AxiosResponse<any>> =>
|
||||
request.put(`/cert/certs/${id}`, data),
|
||||
request.put(`/cert/cert/${id}`, data),
|
||||
// 证书删除
|
||||
certDelete: (id: number): Promise<AxiosResponse<any>> => request.delete(`/cert/certs/${id}`),
|
||||
certDelete: (id: number): Promise<AxiosResponse<any>> => request.delete(`/cert/cert/${id}`),
|
||||
// 签发
|
||||
obtain: (id: number): Promise<AxiosResponse<any>> => request.post(`/cert/obtain`, { id }),
|
||||
obtain: (id: number): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/cert/cert/${id}/obtain`, { id }),
|
||||
// 续签
|
||||
renew: (id: number): Promise<AxiosResponse<any>> => request.post(`/cert/renew`, { id }),
|
||||
renew: (id: number): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/cert/cert/${id}/renew`, { id }),
|
||||
// 获取 DNS 记录
|
||||
manualDNS: (id: number): Promise<AxiosResponse<any>> => request.post(`/cert/manualDNS`, { id }),
|
||||
manualDNS: (id: number): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/cert/cert/${id}/manualDNS`, { id }),
|
||||
// 部署
|
||||
deploy: (id: number, website_id: number): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/cert/deploy`, { id, website_id })
|
||||
request.post(`/cert/cert/${id}/deploy`, { id, website_id })
|
||||
}
|
||||
|
||||
@@ -5,103 +5,101 @@ import { request } from '@/utils'
|
||||
export default {
|
||||
// 获取容器列表
|
||||
containerList: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/container/list', { params: { page, limit } }),
|
||||
request.get('/container/container', { params: { page, limit } }),
|
||||
// 添加容器
|
||||
containerCreate: (config: any): Promise<AxiosResponse<any>> =>
|
||||
request.post('/container/create', config),
|
||||
request.post('/container/container', config),
|
||||
// 删除容器
|
||||
containerRemove: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/remove`, { id }),
|
||||
request.delete(`/container/container/${id}`),
|
||||
// 启动容器
|
||||
containerStart: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/start`, { id }),
|
||||
request.post(`/container/container/${id}/start`),
|
||||
// 停止容器
|
||||
containerStop: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/stop`, { id }),
|
||||
request.post(`/container/container/${id}/stop`),
|
||||
// 重启容器
|
||||
containerRestart: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/restart`, { id }),
|
||||
request.post(`/container/container/${id}/restart`),
|
||||
// 暂停容器
|
||||
containerPause: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/pause`, { id }),
|
||||
request.post(`/container/container/${id}/pause`),
|
||||
// 恢复容器
|
||||
containerUnpause: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/unpause`, { id }),
|
||||
request.post(`/container/container/${id}/unpause`),
|
||||
// 获取容器详情
|
||||
containerDetail: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/detail`, { params: { id } }),
|
||||
request.get(`/container/container/${id}/detail`),
|
||||
// 杀死容器
|
||||
containerKill: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/kill`, { id }),
|
||||
request.post(`/container/container/${id}/kill`),
|
||||
// 重命名容器
|
||||
containerRename: (id: string, name: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/rename`, { id, name }),
|
||||
request.post(`/container/container/${id}/rename`, { name }),
|
||||
// 获取容器状态
|
||||
containerStats: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/stats`, { params: { id } }),
|
||||
request.get(`/container/container/${id}/stats`),
|
||||
// 检查容器是否存在
|
||||
containerExist: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/exist`, { params: { id } }),
|
||||
request.get(`/container/container/${id}/exist`),
|
||||
// 获取容器日志
|
||||
containerLogs: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/logs`, { params: { id } }),
|
||||
request.get(`/container/container/${id}/logs`),
|
||||
// 清理容器
|
||||
containerPrune: (): Promise<AxiosResponse<any>> => request.post(`/container/prune`),
|
||||
containerPrune: (): Promise<AxiosResponse<any>> => request.post(`/container/container/prune`),
|
||||
// 获取网络列表
|
||||
networkList: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/network/list`, { params: { page, limit } }),
|
||||
request.get(`/container/network`, { params: { page, limit } }),
|
||||
// 创建网络
|
||||
networkCreate: (config: any): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/network/create`, config),
|
||||
request.post(`/container/network`, config),
|
||||
// 删除网络
|
||||
networkRemove: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/network/remove`, { id }),
|
||||
request.delete(`/container/network/${id}`),
|
||||
// 检查网络是否存在
|
||||
networkExist: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/network/exist`, { params: { id } }),
|
||||
request.get(`/container/network/${id}/exist`),
|
||||
// 获取网络详情
|
||||
networkInspect: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/network/inspect`, { params: { id } }),
|
||||
request.get(`/container/network/${id}/inspect`),
|
||||
// 连接容器到网络
|
||||
networkConnect: (config: any): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/network/connect`, config),
|
||||
networkConnect: (network: string, container: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/network/${network}/connect`, container),
|
||||
// 断开容器到网络的连接
|
||||
networkDisconnect: (config: any): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/network/disconnect`, config),
|
||||
networkDisconnect: (network: string, container: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/network/${network}/disconnect`, container),
|
||||
// 清理网络
|
||||
networkPrune: (): Promise<AxiosResponse<any>> => request.post(`/container/network/prune`),
|
||||
// 获取镜像列表
|
||||
imageList: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/image/list`, { params: { page, limit } }),
|
||||
request.get(`/container/image`, { params: { page, limit } }),
|
||||
// 检查镜像是否存在
|
||||
imageExist: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/image/exist`, { params: { id } }),
|
||||
request.get(`/container/image/${id}/exist`),
|
||||
// 拉取镜像
|
||||
imagePull: (config: any): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/image/pull`, config),
|
||||
imagePull: (config: any): Promise<AxiosResponse<any>> => request.post(`/container/image`, config),
|
||||
// 删除镜像
|
||||
imageRemove: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/image/remove`, { id }),
|
||||
request.delete(`/container/image/${id}`),
|
||||
// 获取镜像详情
|
||||
imageInspect: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/image/inspect`, { params: { id } }),
|
||||
imageInspect: (id: string): Promise<AxiosResponse<any>> => request.get(`/container/image/${id}`),
|
||||
// 清理镜像
|
||||
imagePrune: (): Promise<AxiosResponse<any>> => request.post(`/container/image/prune`),
|
||||
// 获取卷列表
|
||||
volumeList: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/volume/list`, { params: { page, limit } }),
|
||||
request.get(`/container/volume`, { params: { page, limit } }),
|
||||
// 创建卷
|
||||
volumeCreate: (config: any): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/volume/create`, config),
|
||||
request.post(`/container/volume`, config),
|
||||
// 检查卷是否存在
|
||||
volumeExist: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/volume/exist`, { params: { id } }),
|
||||
request.get(`/container/volume/${id}/exist`),
|
||||
// 删除卷
|
||||
volumeRemove: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.post(`/container/volume/remove`, { id }),
|
||||
request.delete(`/container/volume/${id}`),
|
||||
// 获取卷详情
|
||||
volumeInspect: (id: string): Promise<AxiosResponse<any>> =>
|
||||
request.get(`/container/volume/inspect`, { params: { id } }),
|
||||
request.get(`/container/volume/${id}`),
|
||||
// 清理卷
|
||||
volumePrune: (): Promise<AxiosResponse<any>> => request.post(`/container/volume/prune`)
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ export default {
|
||||
group: string
|
||||
): Promise<AxiosResponse<any>> => request.post('/file/permission', { path, mode, owner, group }),
|
||||
// 压缩文件
|
||||
archive: (paths: string[], file: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/file/archive', { paths, file }),
|
||||
compress: (paths: string[], file: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/file/compress', { paths, file }),
|
||||
// 解压文件
|
||||
unArchive: (file: string, path: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/file/unArchive', { file, path }),
|
||||
unCompress: (file: string, path: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/file/unCompress', { file, path }),
|
||||
// 搜索文件
|
||||
search: (keyword: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/file/search', { keyword }),
|
||||
|
||||
@@ -4,16 +4,13 @@ import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
// 开关
|
||||
switch: (monitor: boolean): Promise<AxiosResponse<any>> =>
|
||||
request.post('/monitor/switch', { monitor }),
|
||||
setting: (): Promise<AxiosResponse<any>> => request.get('/monitor/setting'),
|
||||
// 保存天数
|
||||
saveDays: (days: number): Promise<AxiosResponse<any>> =>
|
||||
request.post('/monitor/saveDays', { days }),
|
||||
updateSetting: (enabled: boolean, days: number): Promise<AxiosResponse<any>> =>
|
||||
request.post('/monitor/setting', { enabled, days }),
|
||||
// 清空监控记录
|
||||
clear: (): Promise<AxiosResponse<any>> => request.post('/monitor/clear'),
|
||||
// 监控记录
|
||||
list: (start: number, end: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/monitor/list', { params: { start, end } }),
|
||||
// 开关和天数
|
||||
switchAndDays: (): Promise<AxiosResponse<any>> => request.get('/monitor/switchAndDays')
|
||||
request.get('/monitor/list', { params: { start, end } })
|
||||
}
|
||||
|
||||
@@ -4,32 +4,27 @@ import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
// 获取防火墙状态
|
||||
firewallStatus: (): Promise<AxiosResponse<any>> => request.get('/safe/firewallStatus'),
|
||||
firewallStatus: (): Promise<AxiosResponse<any>> => request.get('/firewall/status'),
|
||||
// 设置防火墙状态
|
||||
setFirewallStatus: (status: boolean): Promise<AxiosResponse<any>> =>
|
||||
request.post('/safe/firewallStatus', { status }),
|
||||
request.post('/firewall/status', { status }),
|
||||
// 获取防火墙规则
|
||||
firewallRules: (page: number, limit: number): Promise<AxiosResponse<any>> =>
|
||||
request.get('/safe/firewallRules', { params: { page, limit } }),
|
||||
request.get('/firewall/rule', { params: { page, limit } }),
|
||||
// 添加防火墙规则
|
||||
addFirewallRule: (port: string, protocol: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/safe/firewallRules', { port, protocol }),
|
||||
addFirewallRule: (port: number, protocol: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/firewall/rule', { port, protocol }),
|
||||
// 删除防火墙规则
|
||||
deleteFirewallRule: (port: string, protocol: string): Promise<AxiosResponse<any>> =>
|
||||
request.delete('/safe/firewallRules', { data: { port, protocol } }),
|
||||
// 获取SSH状态
|
||||
sshStatus: (): Promise<AxiosResponse<any>> => request.get('/safe/sshStatus'),
|
||||
// 设置SSH状态
|
||||
setSshStatus: (status: boolean): Promise<AxiosResponse<any>> =>
|
||||
request.post('/safe/sshStatus', { status }),
|
||||
// 获取SSH端口
|
||||
sshPort: (): Promise<AxiosResponse<any>> => request.get('/safe/sshPort'),
|
||||
// 设置SSH端口
|
||||
setSshPort: (port: number): Promise<AxiosResponse<any>> =>
|
||||
request.post('/safe/sshPort', { port }),
|
||||
deleteFirewallRule: (port: number, protocol: string): Promise<AxiosResponse<any>> =>
|
||||
request.delete('/firewall/rule', { data: { port, protocol } }),
|
||||
// 获取SSH
|
||||
ssh: (): Promise<AxiosResponse<any>> => request.get('/safe/ssh'),
|
||||
// 设置SSH
|
||||
setSsh: (status: boolean, port: number): Promise<AxiosResponse<any>> =>
|
||||
request.post('/safe/ssh', { status, port }),
|
||||
// 获取Ping状态
|
||||
pingStatus: (): Promise<AxiosResponse<any>> => request.get('/safe/pingStatus'),
|
||||
pingStatus: (): Promise<AxiosResponse<any>> => request.get('/safe/ping'),
|
||||
// 设置Ping状态
|
||||
setPingStatus: (status: boolean): Promise<AxiosResponse<any>> =>
|
||||
request.post('/safe/pingStatus', { status })
|
||||
request.post('/safe/ping', { status })
|
||||
}
|
||||
|
||||
@@ -83,23 +83,6 @@
|
||||
"success": "Update successful"
|
||||
}
|
||||
},
|
||||
"eggs": {
|
||||
"count": {
|
||||
"gt10": "What are you doing, ouch!",
|
||||
"gt4": "Are you awesome, Brother Kun?",
|
||||
"gt0": "If you look at it one more time, it will explode."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "About the panel",
|
||||
"tnb": "The TNB development team wishes everyone a happy Dragon Boat Festival in 2024! Never downtime!",
|
||||
"specialThanks": "Special thanks to the following supporters and {supporter}!",
|
||||
"links": {
|
||||
"group": "group",
|
||||
"channel": "channel",
|
||||
"supporter": ""
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"title": "Quick App"
|
||||
}
|
||||
|
||||
@@ -83,23 +83,6 @@
|
||||
"success": "当前已是最新版本"
|
||||
}
|
||||
},
|
||||
"eggs": {
|
||||
"count": {
|
||||
"gt10": "你干嘛,哎呦!",
|
||||
"gt4": "厉不厉害你坤哥",
|
||||
"gt0": "在多一眼看一眼就会爆炸"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "关于面板",
|
||||
"tnb": "树新蜂开发组祝大家 2024 端午安康!永不宕机!",
|
||||
"specialThanks": "特别感谢以下支持者和 {supporter}!",
|
||||
"links": {
|
||||
"group": "群",
|
||||
"channel": "频道",
|
||||
"supporter": ""
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"title": "快捷应用"
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ const getFilename = (path: string) => {
|
||||
return parts.pop()!
|
||||
}
|
||||
|
||||
const isArchive = (name: string) => {
|
||||
const isCompress = (name: string) => {
|
||||
const ext = getExt(name)
|
||||
return ['zip', 'rar', '7z', 'tar', 'gz'].includes(ext)
|
||||
}
|
||||
@@ -323,7 +323,7 @@ export {
|
||||
getExt,
|
||||
getFilename,
|
||||
getIconByExt,
|
||||
isArchive,
|
||||
isCompress,
|
||||
languageByPath,
|
||||
lastDirectory
|
||||
}
|
||||
|
||||
@@ -10,31 +10,31 @@ import {
|
||||
} from 'naive-ui'
|
||||
|
||||
import cert from '@/api/panel/cert'
|
||||
import type { User } from '@/views/cert/types'
|
||||
import type { Account } from '@/views/cert/types'
|
||||
|
||||
let messageReactive: MessageReactive | null = null
|
||||
const addUserModel = ref<any>({
|
||||
const addAccountModel = ref<any>({
|
||||
hmac_encoded: '',
|
||||
email: '',
|
||||
kid: '',
|
||||
key_type: 'P256',
|
||||
ca: 'letsencrypt'
|
||||
})
|
||||
const updateUserModel = ref<any>({
|
||||
const updateAccountModel = ref<any>({
|
||||
hmac_encoded: '',
|
||||
email: '',
|
||||
kid: '',
|
||||
key_type: 'P256',
|
||||
ca: 'letsencrypt'
|
||||
})
|
||||
const addUserModal = ref(false)
|
||||
const updateUserModal = ref(false)
|
||||
const updateUser = ref<any>()
|
||||
const addAccountModal = ref(false)
|
||||
const updateAccountModal = ref(false)
|
||||
const updateAccount = ref<any>()
|
||||
|
||||
const caProviders = ref<any>([])
|
||||
const algorithms = ref<any>([])
|
||||
|
||||
const userColumns: any = [
|
||||
const accountColumns: any = [
|
||||
{ title: '邮箱', key: 'email', resizable: true, ellipsis: { tooltip: true } },
|
||||
{
|
||||
title: 'CA',
|
||||
@@ -86,13 +86,13 @@ const userColumns: any = [
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
onClick: () => {
|
||||
updateUser.value = row.id
|
||||
updateUserModel.value.email = row.email
|
||||
updateUserModel.value.hmac_encoded = row.hmac_encoded
|
||||
updateUserModel.value.kid = row.kid
|
||||
updateUserModel.value.key_type = row.key_type
|
||||
updateUserModel.value.ca = row.ca
|
||||
updateUserModal.value = true
|
||||
updateAccount.value = row.id
|
||||
updateAccountModel.value.email = row.email
|
||||
updateAccountModel.value.hmac_encoded = row.hmac_encoded
|
||||
updateAccountModel.value.kid = row.kid
|
||||
updateAccountModel.value.key_type = row.key_type
|
||||
updateAccountModel.value.ca = row.ca
|
||||
updateAccountModal.value = true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -103,9 +103,9 @@ const userColumns: any = [
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: async () => {
|
||||
await cert.userDelete(row.id)
|
||||
await cert.accountDelete(row.id)
|
||||
window.$message.success('删除成功')
|
||||
onUserPageChange(1)
|
||||
onAccountPageChange(1)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -131,9 +131,9 @@ const userColumns: any = [
|
||||
}
|
||||
}
|
||||
]
|
||||
const userData = ref<User[]>([] as User[])
|
||||
const accountData = ref<Account[]>([] as Account[])
|
||||
|
||||
const userPagination = reactive({
|
||||
const accountPagination = reactive({
|
||||
page: 1,
|
||||
pageCount: 1,
|
||||
pageSize: 10,
|
||||
@@ -143,51 +143,51 @@ const userPagination = reactive({
|
||||
pageSizes: [10, 20, 50, 100]
|
||||
})
|
||||
|
||||
const onUserPageChange = (page: number) => {
|
||||
userPagination.page = page
|
||||
getUserList(page, userPagination.pageSize).then((res) => {
|
||||
userData.value = res.items
|
||||
userPagination.itemCount = res.total
|
||||
userPagination.pageCount = res.total / userPagination.pageSize + 1
|
||||
const onAccountPageChange = (page: number) => {
|
||||
accountPagination.page = page
|
||||
getAccountList(page, accountPagination.pageSize).then((res) => {
|
||||
accountData.value = res.items
|
||||
accountPagination.itemCount = res.total
|
||||
accountPagination.pageCount = res.total / accountPagination.pageSize + 1
|
||||
})
|
||||
}
|
||||
|
||||
const onUserPageSizeChange = (pageSize: number) => {
|
||||
userPagination.pageSize = pageSize
|
||||
onUserPageChange(1)
|
||||
const onAccountPageSizeChange = (pageSize: number) => {
|
||||
accountPagination.pageSize = pageSize
|
||||
onAccountPageChange(1)
|
||||
}
|
||||
|
||||
const getUserList = async (page: number, limit: number) => {
|
||||
const { data } = await cert.users(page, limit)
|
||||
const getAccountList = async (page: number, limit: number) => {
|
||||
const { data } = await cert.accounts(page, limit)
|
||||
return data
|
||||
}
|
||||
|
||||
const handleAddUser = async () => {
|
||||
const handleAddAccount = async () => {
|
||||
messageReactive = window.$message.loading('正在向 CA 注册账号,请耐心等待', {
|
||||
duration: 0
|
||||
})
|
||||
await cert.userAdd(addUserModel.value)
|
||||
await cert.accountAdd(addAccountModel.value)
|
||||
messageReactive.destroy()
|
||||
window.$message.success('添加成功')
|
||||
addUserModal.value = false
|
||||
onUserPageChange(1)
|
||||
addUserModel.value.email = ''
|
||||
addUserModel.value.hmac_encoded = ''
|
||||
addUserModel.value.kid = ''
|
||||
addAccountModal.value = false
|
||||
onAccountPageChange(1)
|
||||
addAccountModel.value.email = ''
|
||||
addAccountModel.value.hmac_encoded = ''
|
||||
addAccountModel.value.kid = ''
|
||||
}
|
||||
|
||||
const handleUpdateUser = async () => {
|
||||
const handleUpdateAccount = async () => {
|
||||
messageReactive = window.$message.loading('正在向 CA 注册账号,请耐心等待', {
|
||||
duration: 0
|
||||
})
|
||||
await cert.userUpdate(updateUser.value, updateUserModel.value)
|
||||
await cert.accountUpdate(updateAccount.value, updateAccountModel.value)
|
||||
messageReactive.destroy()
|
||||
window.$message.success('更新成功')
|
||||
updateUserModal.value = false
|
||||
onUserPageChange(1)
|
||||
updateUserModel.value.email = ''
|
||||
updateUserModel.value.hmac_encoded = ''
|
||||
updateUserModel.value.kid = ''
|
||||
updateAccountModal.value = false
|
||||
onAccountPageChange(1)
|
||||
updateAccountModel.value.email = ''
|
||||
updateAccountModel.value.hmac_encoded = ''
|
||||
updateAccountModel.value.kid = ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@@ -207,7 +207,7 @@ onMounted(() => {
|
||||
})
|
||||
}
|
||||
})
|
||||
onUserPageChange(1)
|
||||
onAccountPageChange(1)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -215,7 +215,7 @@ onMounted(() => {
|
||||
<n-space vertical size="large">
|
||||
<n-card rounded-10>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="addUserModal = true"> 添加账号 </n-button>
|
||||
<n-button type="primary" @click="addAccountModal = true"> 添加账号 </n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
<n-data-table
|
||||
@@ -223,16 +223,16 @@ onMounted(() => {
|
||||
remote
|
||||
:loading="false"
|
||||
:scroll-x="1200"
|
||||
:columns="userColumns"
|
||||
:data="userData"
|
||||
:columns="accountColumns"
|
||||
:data="accountData"
|
||||
:row-key="(row: any) => row.id"
|
||||
:pagination="userPagination"
|
||||
@update:page="onUserPageChange"
|
||||
@update:page-size="onUserPageSizeChange"
|
||||
:pagination="accountPagination"
|
||||
@update:page="onAccountPageChange"
|
||||
@update:page-size="onAccountPageSizeChange"
|
||||
/>
|
||||
</n-space>
|
||||
<n-modal
|
||||
v-model:show="addUserModal"
|
||||
v-model:show="addAccountModal"
|
||||
preset="card"
|
||||
title="添加账号"
|
||||
style="width: 60vw"
|
||||
@@ -245,10 +245,10 @@ onMounted(() => {
|
||||
<n-alert type="warning">
|
||||
境内无法使用 Google CA,其他 CA 视网络情况而定,建议使用 Let's Encrypt
|
||||
</n-alert>
|
||||
<n-form :model="addUserModel">
|
||||
<n-form :model="addAccountModel">
|
||||
<n-form-item path="ca" label="CA">
|
||||
<n-select
|
||||
v-model:value="addUserModel.ca"
|
||||
v-model:value="addAccountModel.ca"
|
||||
placeholder="选择 CA"
|
||||
clearable
|
||||
:options="caProviders"
|
||||
@@ -256,7 +256,7 @@ onMounted(() => {
|
||||
</n-form-item>
|
||||
<n-form-item path="key_type" label="密钥类型">
|
||||
<n-select
|
||||
v-model:value="addUserModel.key_type"
|
||||
v-model:value="addAccountModel.key_type"
|
||||
placeholder="选择密钥类型"
|
||||
clearable
|
||||
:options="algorithms"
|
||||
@@ -264,7 +264,7 @@ onMounted(() => {
|
||||
</n-form-item>
|
||||
<n-form-item path="email" label="邮箱">
|
||||
<n-input
|
||||
v-model:value="addUserModel.email"
|
||||
v-model:value="addAccountModel.email"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入邮箱地址"
|
||||
@@ -272,7 +272,7 @@ onMounted(() => {
|
||||
</n-form-item>
|
||||
<n-form-item path="kid" label="KID">
|
||||
<n-input
|
||||
v-model:value="addUserModel.kid"
|
||||
v-model:value="addAccountModel.kid"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入 KID"
|
||||
@@ -280,18 +280,18 @@ onMounted(() => {
|
||||
</n-form-item>
|
||||
<n-form-item path="hmac_encoded" label="HMAC">
|
||||
<n-input
|
||||
v-model:value="addUserModel.hmac_encoded"
|
||||
v-model:value="addAccountModel.hmac_encoded"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入 HMAC"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-button type="info" block @click="handleAddUser">提交</n-button>
|
||||
<n-button type="info" block @click="handleAddAccount">提交</n-button>
|
||||
</n-space>
|
||||
</n-modal>
|
||||
<n-modal
|
||||
v-model:show="updateUserModal"
|
||||
v-model:show="updateAccountModal"
|
||||
preset="card"
|
||||
title="修改账号"
|
||||
style="width: 60vw"
|
||||
@@ -304,10 +304,10 @@ onMounted(() => {
|
||||
<n-alert type="warning">
|
||||
境内无法使用 Google CA,其他 CA 视网络情况而定,建议使用 Let's Encrypt
|
||||
</n-alert>
|
||||
<n-form :model="updateUserModel">
|
||||
<n-form :model="updateAccountModel">
|
||||
<n-form-item path="ca" label="CA">
|
||||
<n-select
|
||||
v-model:value="updateUserModel.ca"
|
||||
v-model:value="updateAccountModel.ca"
|
||||
placeholder="选择 CA"
|
||||
clearable
|
||||
:options="caProviders"
|
||||
@@ -315,7 +315,7 @@ onMounted(() => {
|
||||
</n-form-item>
|
||||
<n-form-item path="key_type" label="密钥类型">
|
||||
<n-select
|
||||
v-model:value="updateUserModel.key_type"
|
||||
v-model:value="updateAccountModel.key_type"
|
||||
placeholder="选择密钥类型"
|
||||
clearable
|
||||
:options="algorithms"
|
||||
@@ -323,7 +323,7 @@ onMounted(() => {
|
||||
</n-form-item>
|
||||
<n-form-item path="email" label="邮箱">
|
||||
<n-input
|
||||
v-model:value="updateUserModel.email"
|
||||
v-model:value="updateAccountModel.email"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入邮箱地址"
|
||||
@@ -331,7 +331,7 @@ onMounted(() => {
|
||||
</n-form-item>
|
||||
<n-form-item path="kid" label="KID">
|
||||
<n-input
|
||||
v-model:value="updateUserModel.kid"
|
||||
v-model:value="updateAccountModel.kid"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入 KID"
|
||||
@@ -339,14 +339,14 @@ onMounted(() => {
|
||||
</n-form-item>
|
||||
<n-form-item path="hmac_encoded" label="HMAC">
|
||||
<n-input
|
||||
v-model:value="updateUserModel.hmac_encoded"
|
||||
v-model:value="updateAccountModel.hmac_encoded"
|
||||
type="text"
|
||||
@keydown.enter.prevent
|
||||
placeholder="输入 HMAC"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-button type="info" block @click="handleUpdateUser">提交</n-button>
|
||||
<n-button type="info" block @click="handleUpdateAccount">提交</n-button>
|
||||
</n-space>
|
||||
</n-modal>
|
||||
</template>
|
||||
@@ -11,7 +11,7 @@ const addCertModel = ref<any>({
|
||||
domains: [],
|
||||
dns_id: 0,
|
||||
type: 'P256',
|
||||
user_id: null,
|
||||
account_id: null,
|
||||
website_id: 0,
|
||||
auto_renew: true
|
||||
})
|
||||
@@ -19,7 +19,7 @@ const updateCertModel = ref<any>({
|
||||
domains: [],
|
||||
dns_id: 0,
|
||||
type: 'P256',
|
||||
user_id: null,
|
||||
account_id: null,
|
||||
website_id: 0,
|
||||
auto_renew: true
|
||||
})
|
||||
@@ -40,7 +40,7 @@ const deployCertModel = ref<any>({
|
||||
const algorithms = ref<any>([])
|
||||
const websites = ref<any>([])
|
||||
const dns = ref<any>([])
|
||||
const users = ref<any>([])
|
||||
const accounts = ref<any>([])
|
||||
|
||||
const certColumns: any = [
|
||||
{
|
||||
@@ -94,7 +94,7 @@ const certColumns: any = [
|
||||
},
|
||||
{
|
||||
title: '关联账号',
|
||||
key: 'user_id',
|
||||
key: 'account_id',
|
||||
width: 200,
|
||||
resizable: true,
|
||||
ellipsis: { tooltip: true },
|
||||
@@ -102,11 +102,11 @@ const certColumns: any = [
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.user == null ? 'error' : 'success',
|
||||
type: row.account == null ? 'error' : 'success',
|
||||
bordered: false
|
||||
},
|
||||
{
|
||||
default: () => (row.user?.email == null ? '无' : row.user.email)
|
||||
default: () => (row.account?.email == null ? '无' : row.account.email)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -314,7 +314,7 @@ const certColumns: any = [
|
||||
updateCertModel.value.domains = row.domains
|
||||
updateCertModel.value.dns_id = row.dns_id
|
||||
updateCertModel.value.type = row.type
|
||||
updateCertModel.value.user_id = row.user_id
|
||||
updateCertModel.value.account_id = row.account_id
|
||||
updateCertModel.value.website_id = row.website_id
|
||||
updateCertModel.value.auto_renew = row.auto_renew
|
||||
updateCertModal.value = true
|
||||
@@ -395,7 +395,7 @@ const handleAddCert = async () => {
|
||||
addCertModel.value.domains = []
|
||||
addCertModel.value.dns_id = 0
|
||||
addCertModel.value.type = 'P256'
|
||||
addCertModel.value.user_id = 0
|
||||
addCertModel.value.account_id = 0
|
||||
addCertModel.value.website_id = 0
|
||||
addCertModel.value.auto_renew = true
|
||||
await getAsyncData()
|
||||
@@ -409,7 +409,7 @@ const handleUpdateCert = async () => {
|
||||
updateCertModel.value.domains = []
|
||||
updateCertModel.value.dns_id = 0
|
||||
updateCertModel.value.type = 'P256'
|
||||
updateCertModel.value.user_id = 0
|
||||
updateCertModel.value.account_id = 0
|
||||
updateCertModel.value.website_id = 0
|
||||
updateCertModel.value.auto_renew = true
|
||||
await getAsyncData()
|
||||
@@ -459,10 +459,10 @@ const getAsyncData = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
const { data: userData } = await cert.users(1, 10000)
|
||||
users.value = []
|
||||
for (const item of userData.items) {
|
||||
users.value.push({
|
||||
const { data: accountData } = await cert.accounts(1, 10000)
|
||||
accounts.value = []
|
||||
for (const item of accountData.items) {
|
||||
accounts.value.push({
|
||||
label: item.email,
|
||||
value: item.id
|
||||
})
|
||||
@@ -539,15 +539,15 @@ onMounted(() => {
|
||||
:options="websites"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="user_id" label="账号">
|
||||
<n-form-item path="account_id" label="账号">
|
||||
<n-select
|
||||
v-model:value="addCertModel.user_id"
|
||||
v-model:value="addCertModel.account_id"
|
||||
placeholder="选择用于签发证书的账号"
|
||||
clearable
|
||||
:options="users"
|
||||
:options="accounts"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="user_id" label="DNS">
|
||||
<n-form-item path="account_id" label="DNS">
|
||||
<n-select
|
||||
v-model:value="addCertModel.dns_id"
|
||||
placeholder="选择用于签发证书的DNS"
|
||||
@@ -598,15 +598,15 @@ onMounted(() => {
|
||||
:options="websites"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="user_id" label="账号">
|
||||
<n-form-item path="account_id" label="账号">
|
||||
<n-select
|
||||
v-model:value="updateCertModel.user_id"
|
||||
v-model:value="updateCertModel.account_id"
|
||||
placeholder="选择用于签发证书的账号"
|
||||
clearable
|
||||
:options="users"
|
||||
:options="accounts"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item path="user_id" label="DNS">
|
||||
<n-form-item path="account_id" label="DNS">
|
||||
<n-select
|
||||
v-model:value="updateCertModel.dns_id"
|
||||
placeholder="选择用于签发证书的DNS"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import AccountView from '@/views/cert/AccountView.vue'
|
||||
import CertView from '@/views/cert/CertView.vue'
|
||||
import DNSView from '@/views/cert/DNSView.vue'
|
||||
import UserView from '@/views/cert/UserView.vue'
|
||||
import DnsView from '@/views/cert/DnsView.vue'
|
||||
|
||||
const currentTab = ref('cert')
|
||||
</script>
|
||||
@@ -10,13 +10,13 @@ const currentTab = ref('cert')
|
||||
<common-page show-footer>
|
||||
<n-tabs v-model:value="currentTab" type="line" animated>
|
||||
<n-tab-pane name="cert" tab="证书列表">
|
||||
<CertView />
|
||||
<cert-view />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="user" tab="账号列表">
|
||||
<UserView />
|
||||
<account-view />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="dns" tab="DNS 列表">
|
||||
<DNSView />
|
||||
<dns-view />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</common-page>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export interface Cert {
|
||||
id: number
|
||||
user_id: number
|
||||
account_id: number
|
||||
website_id: number
|
||||
dns_id: number
|
||||
type: string
|
||||
@@ -13,7 +13,7 @@ export interface Cert {
|
||||
updated_at: string
|
||||
website: Website
|
||||
dns: DNS
|
||||
user: User
|
||||
account: Account
|
||||
}
|
||||
|
||||
export interface Website {
|
||||
@@ -43,7 +43,7 @@ export interface DNS {
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface User {
|
||||
export interface Account {
|
||||
id: number
|
||||
email: string
|
||||
ca: string
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { NButton, NInput } from 'naive-ui'
|
||||
|
||||
import * as api from '@/api/panel/file'
|
||||
import api from '@/api/panel/file'
|
||||
import { generateRandomString, getBase } from '@/utils'
|
||||
import EventBus from '@/utils/event'
|
||||
|
||||
@@ -31,8 +31,8 @@ const handleArchive = async () => {
|
||||
const message = window.$message.loading('正在压缩中...', {
|
||||
duration: 0
|
||||
})
|
||||
await api.default
|
||||
.archive(selected.value, file.value)
|
||||
await api
|
||||
.compress(selected.value, file.value)
|
||||
.then(() => {
|
||||
window.$message.success('压缩成功')
|
||||
show.value = false
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import ArchiveModal from '@/views/file/ArchiveModal.vue'
|
||||
import CompressModal from '@/views/file/CompressModal.vue'
|
||||
import ListTable from '@/views/file/ListTable.vue'
|
||||
import PathInput from '@/views/file/PathInput.vue'
|
||||
import PermissionModal from '@/views/file/PermissionModal.vue'
|
||||
@@ -10,7 +10,7 @@ const path = ref('/www')
|
||||
const selected = ref<string[]>([])
|
||||
const marked = ref<Marked[]>([])
|
||||
|
||||
const archive = ref(false)
|
||||
const compress = ref(false)
|
||||
const permission = ref(false)
|
||||
</script>
|
||||
|
||||
@@ -22,17 +22,17 @@ const permission = ref(false)
|
||||
v-model:path="path"
|
||||
v-model:selected="selected"
|
||||
v-model:marked="marked"
|
||||
v-model:archive="archive"
|
||||
v-model:compress="compress"
|
||||
v-model:permission="permission"
|
||||
/>
|
||||
<list-table
|
||||
v-model:path="path"
|
||||
v-model:selected="selected"
|
||||
v-model:marked="marked"
|
||||
v-model:archive="archive"
|
||||
v-model:compress="compress"
|
||||
v-model:permission="permission"
|
||||
/>
|
||||
<archive-modal v-model:show="archive" v-model:path="path" v-model:selected="selected" />
|
||||
<compress-modal v-model:show="compress" v-model:path="path" v-model:selected="selected" />
|
||||
<permission-modal v-model:show="permission" v-model:selected="selected" />
|
||||
</n-flex>
|
||||
</common-page>
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { RowData } from 'naive-ui/es/data-table/src/interface'
|
||||
import file from '@/api/panel/file'
|
||||
import TheIcon from '@/components/custom/TheIcon.vue'
|
||||
import EventBus from '@/utils/event'
|
||||
import { checkName, checkPath, getExt, getFilename, getIconByExt, isArchive } from '@/utils/file'
|
||||
import { checkName, checkPath, getExt, getFilename, getIconByExt, isCompress } from '@/utils/file'
|
||||
import EditModal from '@/views/file/EditModal.vue'
|
||||
import type { Marked } from '@/views/file/types'
|
||||
|
||||
@@ -14,7 +14,7 @@ const loading = ref(false)
|
||||
const path = defineModel<string>('path', { type: String, required: true })
|
||||
const selected = defineModel<any[]>('selected', { type: Array, default: () => [] })
|
||||
const marked = defineModel<Marked[]>('marked', { type: Array, default: () => [] })
|
||||
const archive = defineModel<boolean>('archive', { type: Boolean, required: true })
|
||||
const compress = defineModel<boolean>('compress', { type: Boolean, required: true })
|
||||
const permission = defineModel<boolean>('permission', { type: Boolean, required: true })
|
||||
const editorModal = ref(false)
|
||||
const editorFile = ref('')
|
||||
@@ -24,8 +24,8 @@ const renameModel = ref({
|
||||
source: '',
|
||||
target: ''
|
||||
})
|
||||
const unArchiveModal = ref(false)
|
||||
const unArchiveModel = ref({
|
||||
const unCompressModal = ref(false)
|
||||
const unCompressModel = ref({
|
||||
path: '',
|
||||
file: ''
|
||||
})
|
||||
@@ -125,7 +125,7 @@ const columns: DataTableColumns<RowData> = [
|
||||
onClick: () => {
|
||||
if (row.dir) {
|
||||
selected.value = [row.full]
|
||||
archive.value = true
|
||||
compress.value = true
|
||||
} else {
|
||||
window.open('/api/panel/file/download?path=' + encodeURIComponent(row.full))
|
||||
}
|
||||
@@ -189,8 +189,8 @@ const columns: DataTableColumns<RowData> = [
|
||||
{ label: '复制', value: 'copy' },
|
||||
{ label: '移动', value: 'move' },
|
||||
{ label: '权限', value: 'permission' },
|
||||
{ label: '压缩', value: 'archive' },
|
||||
{ label: '解压', value: 'unarchive', disabled: !isArchive(row.name) }
|
||||
{ label: '压缩', value: 'compress' },
|
||||
{ label: '解压', value: 'uncompress', disabled: !isCompress(row.name) }
|
||||
],
|
||||
onUpdateValue: (value) => {
|
||||
switch (value) {
|
||||
@@ -218,14 +218,14 @@ const columns: DataTableColumns<RowData> = [
|
||||
selected.value = [row.full]
|
||||
permission.value = true
|
||||
break
|
||||
case 'archive':
|
||||
case 'compress':
|
||||
selected.value = [row.full]
|
||||
archive.value = true
|
||||
compress.value = true
|
||||
break
|
||||
case 'unarchive':
|
||||
unArchiveModel.value.file = row.full
|
||||
unArchiveModel.value.path = path.value
|
||||
unArchiveModal.value = true
|
||||
case 'uncompress':
|
||||
unCompressModel.value.file = row.full
|
||||
unCompressModel.value.path = path.value
|
||||
unCompressModal.value = true
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -306,11 +306,11 @@ const handleRename = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleUnArchive = () => {
|
||||
const handleUnCompress = () => {
|
||||
// 移除首位的 / 去检测
|
||||
if (
|
||||
!unArchiveModel.value.path.startsWith('/') ||
|
||||
!checkPath(unArchiveModel.value.path.slice(1))
|
||||
!unCompressModel.value.path.startsWith('/') ||
|
||||
!checkPath(unCompressModel.value.path.slice(1))
|
||||
) {
|
||||
window.$message.error('路径不合法')
|
||||
return
|
||||
@@ -319,11 +319,11 @@ const handleUnArchive = () => {
|
||||
duration: 0
|
||||
})
|
||||
file
|
||||
.unArchive(unArchiveModel.value.file, unArchiveModel.value.path)
|
||||
.unCompress(unCompressModel.value.file, unCompressModel.value.path)
|
||||
.then(() => {
|
||||
message?.destroy()
|
||||
window.$message.success('解压成功')
|
||||
unArchiveModal.value = false
|
||||
unCompressModal.value = false
|
||||
EventBus.emit('file:refresh')
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -391,9 +391,9 @@ onUnmounted(() => {
|
||||
</n-flex>
|
||||
</n-modal>
|
||||
<n-modal
|
||||
v-model:show="unArchiveModal"
|
||||
v-model:show="unCompressModal"
|
||||
preset="card"
|
||||
:title="'解压缩 - ' + unArchiveModel.file"
|
||||
:title="'解压缩 - ' + unCompressModel.file"
|
||||
style="width: 60vw"
|
||||
size="huge"
|
||||
:bordered="false"
|
||||
@@ -402,10 +402,10 @@ onUnmounted(() => {
|
||||
<n-flex vertical>
|
||||
<n-form>
|
||||
<n-form-item label="解压到">
|
||||
<n-input v-model:value="unArchiveModel.path" />
|
||||
<n-input v-model:value="unCompressModel.path" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-button type="primary" @click="handleUnArchive">解压</n-button>
|
||||
<n-button type="primary" @click="handleUnCompress">解压</n-button>
|
||||
</n-flex>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Marked } from '@/views/file/types'
|
||||
const path = defineModel<string>('path', { type: String, required: true })
|
||||
const selected = defineModel<string[]>('selected', { type: Array, default: () => [] })
|
||||
const marked = defineModel<Marked[]>('marked', { type: Array, default: () => [] })
|
||||
const archive = defineModel<boolean>('archive', { type: Boolean, required: true })
|
||||
const compress = defineModel<boolean>('compress', { type: Boolean, required: true })
|
||||
const permission = defineModel<boolean>('permission', { type: Boolean, required: true })
|
||||
|
||||
const upload = ref(false)
|
||||
@@ -126,7 +126,7 @@ const bulkDelete = () => {
|
||||
<n-button-group v-if="selected.length">
|
||||
<n-button @click="handleCopy"> 复制 </n-button>
|
||||
<n-button @click="handleMove"> 移动 </n-button>
|
||||
<n-button @click="archive = true"> 压缩 </n-button>
|
||||
<n-button @click="compress = true"> 压缩 </n-button>
|
||||
<n-button @click="permission = true"> 权限 </n-button>
|
||||
<n-popconfirm @positive-click="bulkDelete">
|
||||
<template #trigger>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { UploadCustomRequestOptions } from 'naive-ui'
|
||||
|
||||
import * as api from '@/api/panel/file'
|
||||
import api from '@/api/panel/file'
|
||||
import EventBus from '@/utils/event'
|
||||
|
||||
const show = defineModel<boolean>('show', { type: Boolean, required: true })
|
||||
@@ -11,7 +11,7 @@ const upload = ref<any>(null)
|
||||
const uploadRequest = ({ file, onFinish, onError, onProgress }: UploadCustomRequestOptions) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file.file as File)
|
||||
api.default
|
||||
api
|
||||
.upload(`${path.value}/${file.name}`, formData, onProgress)
|
||||
.then(() => {
|
||||
window.$message.success(`上传 ${file.name} 成功`)
|
||||
|
||||
@@ -112,18 +112,6 @@ const handleUpdate = () => {
|
||||
})
|
||||
}
|
||||
|
||||
let eggCount = 0
|
||||
const getEgg = () => {
|
||||
eggCount++
|
||||
if (eggCount > 10) {
|
||||
return t('homeIndex.eggs.count.gt10')
|
||||
} else if (eggCount > 4) {
|
||||
return t('homeIndex.eggs.count.gt4')
|
||||
} else {
|
||||
return t('homeIndex.eggs.count.gt0')
|
||||
}
|
||||
}
|
||||
|
||||
const toSponsor = () => {
|
||||
if (locale.value === 'en') {
|
||||
window.open('https://opencollective.com/tnb')
|
||||
@@ -540,101 +528,49 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-grid
|
||||
x-gap="12"
|
||||
y-gap="12"
|
||||
cols="1 s:1 m:2 l:3 xl:3 2xl:3"
|
||||
item-responsive
|
||||
responsive="screen"
|
||||
>
|
||||
<n-gi span="2 s:1 m:1 l:2">
|
||||
<div min-w-375 flex-1>
|
||||
<n-card :segmented="true" rounded-10 size="small" :title="$t('homeIndex.apps.title')">
|
||||
<n-grid
|
||||
v-if="homeApps"
|
||||
x-gap="12"
|
||||
y-gap="12"
|
||||
cols="3 s:1 m:2 l:3"
|
||||
item-responsive
|
||||
responsive="screen"
|
||||
|
||||
<div min-w-375 flex-1>
|
||||
<n-card :segmented="true" rounded-10 size="small" :title="$t('homeIndex.apps.title')">
|
||||
<n-grid
|
||||
v-if="homeApps"
|
||||
x-gap="12"
|
||||
y-gap="12"
|
||||
cols="3 s:1 m:2 l:3"
|
||||
item-responsive
|
||||
responsive="screen"
|
||||
>
|
||||
<n-gi v-for="item in homeApps" :key="item.name">
|
||||
<n-card
|
||||
:segmented="true"
|
||||
size="small"
|
||||
cursor-pointer
|
||||
rounded-10
|
||||
hover:card-shadow
|
||||
@click="handleManageApp(item.slug)"
|
||||
>
|
||||
<n-gi v-for="item in homeApps" :key="item.name">
|
||||
<n-card
|
||||
:segmented="true"
|
||||
size="small"
|
||||
cursor-pointer
|
||||
rounded-10
|
||||
hover:card-shadow
|
||||
@click="handleManageApp(item.slug)"
|
||||
>
|
||||
<n-space>
|
||||
<n-thing>
|
||||
<template #avatar>
|
||||
<n-avatar class="mt-4">
|
||||
<n-icon>
|
||||
<icon-mdi:package-variant-closed />
|
||||
</n-icon>
|
||||
</n-avatar>
|
||||
</template>
|
||||
<template #header>
|
||||
{{ item.name }}
|
||||
</template>
|
||||
<template #description>
|
||||
{{ item.version }}
|
||||
</template>
|
||||
</n-thing>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-skeleton v-else text :repeat="9" />
|
||||
</n-card>
|
||||
</div>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<div min-w-375 flex-1>
|
||||
<n-card
|
||||
:segmented="true"
|
||||
rounded-10
|
||||
size="small"
|
||||
:title="$t('homeIndex.about.title')"
|
||||
>
|
||||
<template #header-extra>
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<n-icon size="20">
|
||||
<icon-mdi:about-circle-outline />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>{{ getEgg() }}</span>
|
||||
</n-popover>
|
||||
</template>
|
||||
<n-space vertical :size="12">
|
||||
<n-alert type="success">
|
||||
{{ $t('homeIndex.about.tnb') }}
|
||||
</n-alert>
|
||||
<n-alert type="info">
|
||||
<span
|
||||
v-html="
|
||||
$t('homeIndex.about.specialThanks', {
|
||||
supporter: `<a target='_blank' href='https://www.weixiaoduo.com/'>「薇晓朵」<\/a>`
|
||||
})
|
||||
"
|
||||
>
|
||||
</span>
|
||||
</n-alert>
|
||||
<n-image
|
||||
src="https://mirror.ghproxy.com/https://raw.githubusercontent.com/TheTNB/sponsor/main/sponsors.svg"
|
||||
width="100%"
|
||||
preview-disabled
|
||||
lazy
|
||||
@click="toSponsor"
|
||||
/>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-space>
|
||||
<n-thing>
|
||||
<template #avatar>
|
||||
<n-avatar class="mt-4">
|
||||
<n-icon>
|
||||
<icon-mdi:package-variant-closed />
|
||||
</n-icon>
|
||||
</n-avatar>
|
||||
</template>
|
||||
<template #header>
|
||||
{{ item.name }}
|
||||
</template>
|
||||
<template #description>
|
||||
{{ item.version }}
|
||||
</template>
|
||||
</n-thing>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-skeleton v-else text :repeat="9" />
|
||||
</n-card>
|
||||
</div>
|
||||
</n-space>
|
||||
</div>
|
||||
</AppPage>
|
||||
|
||||
@@ -6,10 +6,10 @@ import { useI18n } from 'vue-i18n'
|
||||
import info from '@/api/panel/info'
|
||||
import { router } from '@/router'
|
||||
import { formatDateTime } from '@/utils'
|
||||
import type { PanelInfo } from '@/views/home/types'
|
||||
import type { Version } from '@/views/home/types'
|
||||
|
||||
const { t } = useI18n()
|
||||
const versions = ref<PanelInfo[] | null>(null)
|
||||
const versions = ref<Version[] | null>(null)
|
||||
let messageReactive: MessageReactive | null = null
|
||||
|
||||
const getVersions = () => {
|
||||
@@ -75,9 +75,9 @@ onMounted(() => {
|
||||
:type="Number(index) == 0 ? 'info' : 'default'"
|
||||
:key="index"
|
||||
:title="item.version"
|
||||
:time="formatDateTime(item.date)"
|
||||
:time="formatDateTime(item.updated_at)"
|
||||
>
|
||||
<pre v-html="item.body" />
|
||||
<pre v-html="item.description" />
|
||||
</n-timeline-item>
|
||||
</n-timeline>
|
||||
<div v-else pt-40>
|
||||
|
||||
@@ -175,13 +175,17 @@ export interface HomeApp {
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface PanelInfo {
|
||||
name: string
|
||||
version: string
|
||||
download_name: string
|
||||
download_url: string
|
||||
body: string
|
||||
date: string
|
||||
checksums: string
|
||||
checksums_url: string
|
||||
export interface VersionDownload {
|
||||
url: string
|
||||
arch: string
|
||||
checksum: string
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
created_at: string
|
||||
updated_at: string
|
||||
type: string
|
||||
version: string
|
||||
description: string
|
||||
downloads: VersionDownload[]
|
||||
}
|
||||
|
||||
@@ -414,21 +414,15 @@ const getData = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
const getSwitchAndDays = async () => {
|
||||
monitor.switchAndDays().then((res) => {
|
||||
monitorSwitch.value = res.data.switch
|
||||
const getSetting = async () => {
|
||||
monitor.setting().then((res) => {
|
||||
monitorSwitch.value = res.data.enabled
|
||||
saveDay.value = res.data.days
|
||||
})
|
||||
}
|
||||
|
||||
const handleSwitch = async () => {
|
||||
monitor.switch(monitorSwitch.value).then(() => {
|
||||
window.$message.success('操作成功')
|
||||
})
|
||||
}
|
||||
|
||||
const handleDays = async () => {
|
||||
monitor.saveDays(saveDay.value).then(() => {
|
||||
const handleUpdate = async () => {
|
||||
monitor.updateSetting(monitorSwitch.value, saveDay.value).then(() => {
|
||||
window.$message.success('操作成功')
|
||||
})
|
||||
}
|
||||
@@ -469,7 +463,7 @@ watch([start, end], () => {
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getSwitchAndDays()
|
||||
getSetting()
|
||||
getData()
|
||||
})
|
||||
</script>
|
||||
@@ -485,7 +479,7 @@ onMounted(() => {
|
||||
>
|
||||
<n-grid cols="1 s:1 m:1 l:24 xl:24 2xl:24" item-responsive responsive="screen">
|
||||
<n-form-item-gi :span="3" label="开启监控">
|
||||
<n-switch v-model:value="monitorSwitch" @update-value="handleSwitch" />
|
||||
<n-switch v-model:value="monitorSwitch" @update-value="handleUpdate" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="6" label="保存天数">
|
||||
<n-input-number v-model:value="saveDay">
|
||||
@@ -493,7 +487,7 @@ onMounted(() => {
|
||||
</n-input-number>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="2">
|
||||
<n-button type="primary" @click="handleDays">确定</n-button>
|
||||
<n-button type="primary" @click="handleUpdate">确定</n-button>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="9" label="时间选择">
|
||||
<n-date-picker v-model:value="start" type="datetime" />
|
||||
|
||||
@@ -87,7 +87,7 @@ const pagination = reactive({
|
||||
const selectedRowKeys = ref<any>([])
|
||||
|
||||
const addModel = ref({
|
||||
port: '',
|
||||
port: 80,
|
||||
protocol: 'tcp'
|
||||
})
|
||||
|
||||
@@ -105,7 +105,7 @@ const handleDelete = async (row: any) => {
|
||||
const handleAdd = async () => {
|
||||
await safe.addFirewallRule(addModel.value.port, addModel.value.protocol).then(() => {
|
||||
window.$message.success(t('safeIndex.alerts.add'))
|
||||
addModel.value.port = ''
|
||||
addModel.value.port = 80
|
||||
addModel.value.protocol = 'tcp'
|
||||
})
|
||||
getFirewallRules(pagination.page, pagination.pageSize).then((res) => {
|
||||
@@ -124,15 +124,13 @@ const getSetting = async () => {
|
||||
safe.firewallStatus().then((res) => {
|
||||
model.value.firewallStatus = res.data
|
||||
})
|
||||
safe.sshStatus().then((res) => {
|
||||
model.value.sshStatus = res.data
|
||||
safe.ssh().then((res) => {
|
||||
model.value.sshStatus = res.data.status
|
||||
model.value.sshPort = res.data.port
|
||||
})
|
||||
safe.pingStatus().then((res) => {
|
||||
model.value.pingStatus = res.data
|
||||
})
|
||||
safe.sshPort().then((res) => {
|
||||
model.value.sshPort = res.data
|
||||
})
|
||||
}
|
||||
|
||||
const handleFirewallStatus = () => {
|
||||
@@ -141,8 +139,8 @@ const handleFirewallStatus = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleSshStatus = () => {
|
||||
safe.setSshStatus(model.value.sshStatus).then(() => {
|
||||
const handleSsh = () => {
|
||||
safe.setSsh(model.value.sshStatus, model.value.sshPort).then(() => {
|
||||
window.$message.success(t('safeIndex.alerts.setup'))
|
||||
})
|
||||
}
|
||||
@@ -153,12 +151,6 @@ const handlePingStatus = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleSshPort = () => {
|
||||
safe.setSshPort(model.value.sshPort).then(() => {
|
||||
window.$message.success(t('safeIndex.alerts.setup'))
|
||||
})
|
||||
}
|
||||
|
||||
const batchDelete = async () => {
|
||||
if (selectedRowKeys.value.length === 0) {
|
||||
window.$message.info(t('safeIndex.alerts.select'))
|
||||
@@ -231,7 +223,7 @@ onMounted(() => {
|
||||
<n-form-item :label="$t('safeIndex.filter.fields.ssh.label')">
|
||||
<n-switch
|
||||
v-model:value="model.sshStatus"
|
||||
@update:value="handleSshStatus"
|
||||
@update:value="handleSsh"
|
||||
:checkedChildren="$t('safeIndex.filter.fields.ssh.checked')"
|
||||
:unCheckedChildren="$t('safeIndex.filter.fields.ssh.unchecked')"
|
||||
/>
|
||||
@@ -245,7 +237,7 @@ onMounted(() => {
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item :label="$t('safeIndex.filter.fields.port.label')">
|
||||
<n-input-number v-model:value="model.sshPort" @blur="handleSshPort" />
|
||||
<n-input-number v-model:value="model.sshPort" @blur="handleSsh" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
@@ -257,9 +249,11 @@ onMounted(() => {
|
||||
{{ $t('safeIndex.confirm.batchDelete') }}
|
||||
</n-popconfirm>
|
||||
<n-text>{{ $t('safeIndex.portControl.title') }}</n-text>
|
||||
<n-input
|
||||
<n-input-number
|
||||
v-model:value="addModel.port"
|
||||
:placeholder="$t('safeIndex.portControl.fields.port.placeholder')"
|
||||
:min="1"
|
||||
:max="65535"
|
||||
/>
|
||||
<n-select
|
||||
v-model:value="addModel.protocol"
|
||||
|
||||
@@ -191,7 +191,7 @@ const addModel = ref({
|
||||
name: '',
|
||||
domains: [] as Array<string>,
|
||||
ports: [] as Array<number>,
|
||||
php: '0',
|
||||
php: 0,
|
||||
db: false,
|
||||
db_type: '0',
|
||||
db_name: '',
|
||||
@@ -326,7 +326,7 @@ const handleAdd = async () => {
|
||||
name: '',
|
||||
domains: [] as Array<string>,
|
||||
ports: [] as Array<number>,
|
||||
php: '0',
|
||||
php: 0,
|
||||
db: false,
|
||||
db_type: '0',
|
||||
db_name: '',
|
||||
|
||||
Reference in New Issue
Block a user