Compare commits

...
19 Commits
Author SHA1 Message Date
ssongliu 7f9f4ae740 fix: Fix PostgreSQL backup failure with $ character (#11252) 2025-12-08 15:24:08 +08:00
CityFun 8bec6f2b28 fix: Fixed network search returning empty results with no page update (#11251) 2025-12-08 07:10:37 +00:00
CityFun b397363c9d fix: Fixed process search returning empty results with no page update (#11246) 2025-12-08 05:54:36 +00:00
KOMATA acb800b505 fix: Update compose operation handling to ensure correct path usage and detail loading after operations (#11245) 2025-12-08 13:41:55 +08:00
CityFun ec37af2f18 fix: Fixed missing redirect after installing runtime environment (#11243) 2025-12-08 04:44:36 +00:00
CityFun d2b030e698 feat: Added support for auto-filling remote MySQL root password in App Store (#11240) 2025-12-08 11:39:08 +08:00
ssongliu c6f496f2a5 fix: Fix the issue of container compose log cleanup failure (#11238) 2025-12-08 11:38:05 +08:00
CityFun 722e1929f1 fix: Fixed file comparison highlighting loss issue (#11237) 2025-12-08 03:28:35 +00:00
王贺 f9a20ec443 Fix typo (#11229) 2025-12-07 11:36:20 +08:00
ssongliu f5568d4874 fix: Fix style issues caused by element upgrade (#11224) 2025-12-06 14:16:34 +00:00
ssongliu 87bbe4ee12 fix: Fix the issue of abnormal compose directory redirection (#11223) 2025-12-06 14:14:28 +00:00
ssongliu a034b03c1d fix: Task framework adds timeout exit parameter (#11206) 2025-12-05 18:42:23 +08:00
CityFun 470f4e4db7 style: Updated layout page save button styling (#11198) 2025-12-05 03:24:23 +00:00
CityFun 9428efd8c0 fix: Fixed CAPTCHA loading issue in certain scenarios (#11186) 2025-12-04 10:12:22 +00:00
CityFun bf4d91b68b fix: Fixed login CAPTCHA bypass vulnerability (#11185) 2025-12-04 10:10:23 +00:00
KOMATA 781155b029 perf: remove useless stat checking logic (#11178)
* feat: Enhance proxy initialization and error handling

* Add a timeout to the dialer for Unix socket connections
* Improve error response by including the error message in the "Bad Gateway" response

* refactor: Change sockPath variable to constant in proxy initialization

* Update sockPath to a constant SockPath for improved clarity and consistency
* Ensure the new constant is used in the dialer function for Unix socket connections
2025-12-04 18:07:47 +08:00
KOMATA 3caf0ac529 fix: Ensure proper client closure in volume and device clean operations (#11177) 2025-12-04 18:07:16 +08:00
CityFun 7d25228e55 perf: Optimized tensorrtLLM display performanceperf (#11184) 2025-12-04 17:57:19 +08:00
ssongliu 4fb099912e fix: Fix the issue of remote database connection failure (#11181)
Refs #11173
2025-12-04 09:56:23 +00:00
29 changed files with 288 additions and 127 deletions
+22
View File
@@ -393,6 +393,28 @@ func (b *BaseApi) CleanContainerLog(c *gin.Context) {
helper.Success(c)
}
// @Tags Container
// @Summary Clean compose log
// @Accept json
// @Param request body dto.ComposeLogClean true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/compose/clean/log [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清理容器编排 [name] 日志","formatEN":"clean compose [name] logs"}
func (b *BaseApi) CleanComposeLog(c *gin.Context) {
var req dto.ComposeLogClean
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := containerService.ComposeLogClean(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Container
// @Summary Rename Container
// @Accept json
+4
View File
@@ -291,6 +291,10 @@ type ComposeUpdate struct {
Content string `json:"content" validate:"required"`
Env []string `json:"env"`
}
type ComposeLogClean struct {
Name string `json:"name" validate:"required"`
Path string `json:"path" validate:"required"`
}
type ContainerLog struct {
Container string `json:"container" validate:"required"`
+9 -7
View File
@@ -258,6 +258,7 @@ type DatabaseInfo struct {
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
InitialDB string `json:"initialDB"`
Username string `json:"username"`
Password string `json:"password"`
@@ -309,13 +310,14 @@ type DatabaseCreate struct {
}
type DatabaseUpdate struct {
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password"`
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
InitialDB string `json:"initialDB"`
Username string `json:"username" validate:"required"`
Password string `json:"password"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
+1
View File
@@ -39,6 +39,7 @@ type AppItem struct {
Tags []string `json:"tags"`
GpuSupport bool `json:"gpuSupport"`
Recommend int `json:"recommend"`
Type string `json:"type"`
}
type TagDTO struct {
+1
View File
@@ -125,6 +125,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (*response.
GpuSupport: ap.GpuSupport,
Recommend: ap.Recommend,
Description: ap.GetDescription(ctx),
Type: ap.Type,
}
appDTOs = append(appDTOs, appDTO)
tags, err := getAppTags(ap.ID, lang)
+1
View File
@@ -515,6 +515,7 @@ func (a *AppInstallService) GetServices(key string) ([]response.AppService, erro
} else {
service.From = constant.AppResourceRemote
service.Status = constant.StatusRunning
service.Config = map[string]string{"PANEL_DB_ROOT_PASSWORD": db.Password, "PANEL_DB_ROOT_USER": db.Username}
}
res = append(res, service)
}
+1
View File
@@ -85,6 +85,7 @@ type IContainerService interface {
CreateVolume(req dto.VolumeCreate) error
TestCompose(req dto.ComposeCreate) (bool, error)
ComposeUpdate(req dto.ComposeUpdate) error
ComposeLogClean(req dto.ComposeLogClean) error
Prune(req dto.ContainerPrune) error
LoadUsers(req dto.OperationWithName) []string
+52
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"sort"
"strings"
"time"
@@ -284,6 +285,57 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
return nil
}
func (u *ContainerService) ComposeLogClean(req dto.ComposeLogClean) error {
client, err := docker.NewDockerClient()
if err != nil {
return err
}
defer client.Close()
options := container.ListOptions{All: true}
options.Filters = filters.NewArgs()
options.Filters.Add("label", composeProjectLabel)
list, err := client.ContainerList(context.Background(), options)
if err != nil {
return err
}
ctx := context.Background()
for _, item := range list {
if name, ok := item.Labels[composeProjectLabel]; ok {
if name != req.Name {
continue
}
containerItem, err := client.ContainerInspect(ctx, item.ID)
if err != nil {
return err
}
if err := client.ContainerStop(ctx, containerItem.ID, container.StopOptions{}); err != nil {
return err
}
file, err := os.OpenFile(containerItem.LogPath, os.O_RDWR|os.O_CREATE, constant.FilePerm)
if err != nil {
return err
}
defer file.Close()
if err = file.Truncate(0); err != nil {
return err
}
_, _ = file.Seek(0, 0)
files, _ := filepath.Glob(fmt.Sprintf("%s.*", containerItem.LogPath))
for _, file := range files {
_ = os.Remove(file)
}
}
}
return u.ComposeOperation(dto.ComposeOperation{
Name: req.Name,
Path: req.Path,
Operation: "restart",
})
}
func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
if req.From == "template" || req.From == "edit" {
dir := fmt.Sprintf("%s/docker/compose/%s", global.Dir.DataDir, req.Name)
+1
View File
@@ -19,6 +19,7 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
if err != nil {
return 0, nil, err
}
defer client.Close()
list, err := client.VolumeList(context.TODO(), volume.ListOptions{})
if err != nil {
return 0, nil, err
+24 -19
View File
@@ -119,9 +119,10 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
if req.Timeout == 0 {
req.Timeout = 30
}
var err error
switch req.Type {
case constant.AppPostgresql:
_, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
_, err = postgresql.NewPostgresqlClient(pgclient.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
@@ -130,17 +131,15 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
Password: req.Password,
Timeout: req.Timeout,
})
return err == nil
case constant.AppRedis:
_, err := redisclient.NewRedisClient(redisclient.DBInfo{
_, err = redisclient.NewRedisClient(redisclient.DBInfo{
Address: req.Address,
Port: req.Port,
Password: req.Password,
Timeout: req.Timeout,
})
return err == nil
case "mysql", "mariadb":
_, err := mysql.NewMysqlClient(client.DBInfo{
_, err = mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
@@ -154,10 +153,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
SkipVerify: req.SkipVerify,
Timeout: req.Timeout,
})
return err == nil
}
if err != nil {
global.LOG.Errorf("check database connection failed, err: %v", err)
return false
}
return false
return true
}
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
@@ -174,12 +176,13 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
From: "remote",
Address: req.Address,
Port: req.Port,
InitialDB: req.InitialDB,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -275,12 +278,13 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
From: "remote",
Address: req.Address,
Port: req.Port,
InitialDB: req.InitialDB,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -326,6 +330,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
upMap["port"] = req.Port
upMap["username"] = req.Username
upMap["password"] = pass
upMap["initial_db"] = req.InitialDB
upMap["timeout"] = req.Timeout
upMap["description"] = req.Description
upMap["ssl"] = req.SSL
+1 -1
View File
@@ -115,7 +115,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
}
if req.From == "local" && req.Username == "root" {
return nil, errors.New("Cannot set root as user name")
return nil, errors.New("cannot set root as user name")
}
cli, version, err := LoadMysqlClientByFrom(req.Database)
+1 -5
View File
@@ -21,7 +21,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
type PostgresqlService struct{}
@@ -138,10 +137,6 @@ func (u *PostgresqlService) Create(ctx context.Context, req dto.PostgresqlDBCrea
return nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
if req.From == "local" && req.Username == "root" {
return nil, errors.New("Cannot set root as user name")
}
cli, err := LoadPostgresqlClientByFrom(req.Database)
if err != nil {
return nil, err
@@ -185,6 +180,7 @@ func LoadPostgresqlClientByFrom(database string) (postgresql.PostgresqlClient, e
dbInfo.Port = databaseItem.Port
dbInfo.Username = databaseItem.Username
dbInfo.Password = databaseItem.Password
dbInfo.InitialDB = databaseItem.InitialDB
} else {
app, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database)
if err != nil {
+5
View File
@@ -413,6 +413,7 @@ func loadContainerTree() []dto.CleanTree {
if err != nil {
return treeData
}
defer client.Close()
diskUsage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{})
if err != nil {
return treeData
@@ -552,6 +553,7 @@ func dropBuildCache() (int, int) {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
defer client.Close()
opts := build.CachePruneOptions{}
opts.All = true
res, err := client.BuildCachePrune(context.Background(), opts)
@@ -568,6 +570,7 @@ func dropImages() (int, int) {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
defer client.Close()
pruneFilters := filters.NewArgs()
pruneFilters.Add("dangling", "false")
res, err := client.ImagesPrune(context.Background(), pruneFilters)
@@ -584,6 +587,7 @@ func dropContainers() (int, int) {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
defer client.Close()
pruneFilters := filters.NewArgs()
res, err := client.ContainersPrune(context.Background(), pruneFilters)
if err != nil {
@@ -599,6 +603,7 @@ func dropVolumes() (int, int) {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
defer client.Close()
pruneFilters := filters.NewArgs()
versions, err := client.ServerVersion(context.Background())
if err != nil {
+16 -22
View File
@@ -88,31 +88,25 @@ func (t TensorRTLLMService) Page(req request.TensorRTLLMSearch) response.TensorR
}
composeByte, err := files.NewFileOp().GetContent(path.Join(global.Dir.TensorRTLLMDir, item.Name, "docker-compose.yml"))
if err != nil {
continue
}
serverDTO.Environments, err = getDockerComposeEnvironments(composeByte)
if err != nil {
continue
if err == nil {
serverDTO.Environments, _ = getDockerComposeEnvironments(composeByte)
}
volumes, err := getDockerComposeVolumes(composeByte)
if err != nil {
continue
}
var defaultVolumes = map[string]string{
"${MODEL_PATH}": "${MODEL_PATH}",
}
for _, volume := range volumes {
exist := false
for key, value := range defaultVolumes {
if key == volume.Source && value == volume.Target {
exist = true
break
}
if err == nil {
var defaultVolumes = map[string]string{
"${MODEL_PATH}": "${MODEL_PATH}",
}
if !exist {
serverDTO.Volumes = append(serverDTO.Volumes, volume)
for _, volume := range volumes {
exist := false
for key, value := range defaultVolumes {
if key == volume.Source && value == volume.Target {
exist = true
break
}
}
if !exist {
serverDTO.Volumes = append(serverDTO.Volumes, volume)
}
}
}
items = append(items, serverDTO)
+1
View File
@@ -48,6 +48,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
baRouter.POST("/compose", baseApi.CreateCompose)
baRouter.POST("/compose/test", baseApi.TestCompose)
baRouter.POST("/compose/operate", baseApi.OperatorCompose)
baRouter.POST("/compose/clean/log", baseApi.CleanComposeLog)
baRouter.POST("/compose/update", baseApi.ComposeUpdate)
baRouter.GET("/template", baseApi.ListComposeTemplate)
+2 -2
View File
@@ -132,7 +132,7 @@ func (r *Remote) Backup(info BackupInfo) error {
}
fileNameItem := info.TargetDir + "/" + strings.TrimSuffix(info.FileName, ".gz")
backupCommand := exec.Command("bash", "-c",
fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD=\"%s\" pg_dump -h %s -p %d --no-owner -Fc -U %s %s' > %s",
fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD='\\''%s'\\'' pg_dump -h %s -p %d --no-owner -Fc -U %s %s' > %s",
imageTag, r.Password, r.Address, r.Port, r.User, info.Name, fileNameItem))
_ = backupCommand.Run()
b := make([]byte, 5)
@@ -176,7 +176,7 @@ func (r *Remote) Recover(info RecoverInfo) error {
}()
}
recoverCommand := exec.Command("bash", "-c",
fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD=\"%s\" pg_restore -h %s -p %d --verbose --clean --no-privileges --no-owner -Fc -c --if-exists --no-owner -U %s -d %s --role=%s' < %s",
fmt.Sprintf("docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD='\\''%s'\\'' pg_restore -h %s -p %d --verbose --clean --no-privileges --no-owner -Fc -c --if-exists --no-owner -U %s -d %s --role=%s' < %s",
imageTag, r.Password, r.Address, r.Port, r.User, info.Name, info.Username, fileName))
pipe, _ := recoverCommand.StdoutPipe()
stderrPipe, _ := recoverCommand.StderrPipe()
+25 -18
View File
@@ -11,6 +11,7 @@ import (
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/i18n"
@@ -21,27 +22,29 @@ type ActionFunc func(*Task) error
type RollbackFunc func(*Task)
type Task struct {
Name string
TaskID string
Logger *log.Logger
SubTasks []*SubTask
Rollbacks []RollbackFunc
logFile *os.File
taskRepo repo.ITaskRepo
Task *model.Task
ParentID string
Name string
TaskID string
Logger *log.Logger
SubTasks []*SubTask
Rollbacks []RollbackFunc
logFile *os.File
taskRepo repo.ITaskRepo
Task *model.Task
ParentID string
CancelWhenTimeout bool
}
type SubTask struct {
RootTask *Task
Name string
StepAlias string
Retry int
Timeout time.Duration
Action ActionFunc
Rollback RollbackFunc
Error error
IgnoreErr bool
RootTask *Task
Name string
StepAlias string
Retry int
Timeout time.Duration
Action ActionFunc
Rollback RollbackFunc
Error error
IgnoreErr bool
CancelWhenTimeout bool
}
const (
@@ -143,6 +146,9 @@ func (s *SubTask) Execute() error {
select {
case <-ctx.Done():
s.RootTask.Log(i18n.GetWithName("TaskTimeout", subTaskName))
if s.CancelWhenTimeout {
return buserr.New(i18n.GetWithName("TaskTimeout", subTaskName))
}
case err = <-done:
if err != nil {
s.RootTask.Log(i18n.GetWithNameAndErr("SubTaskFailed", subTaskName, err))
@@ -173,6 +179,7 @@ func (t *Task) Execute() error {
var err error
t.Log(i18n.GetWithName("TaskStart", t.Name))
for _, subTask := range t.SubTasks {
subTask.CancelWhenTimeout = t.CancelWhenTimeout
t.Task.CurrentStep = subTask.StepAlias
t.updateTask(t.Task)
if err = subTask.Execute(); err == nil {
+7 -4
View File
@@ -8,15 +8,18 @@ import (
"time"
)
var (
sockPath = "/etc/1panel/agent.sock"
const SockPath = "/etc/1panel/agent.sock"
var (
LocalAgentProxy *httputil.ReverseProxy
)
func Init() {
dialer := &net.Dialer{
Timeout: 5 * time.Second,
}
dialUnix := func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.Dial("unix", sockPath)
return dialer.DialContext(ctx, "unix", SockPath)
}
transport := &http.Transport{
DialContext: dialUnix,
@@ -33,7 +36,7 @@ func Init() {
Transport: transport,
ErrorHandler: func(rw http.ResponseWriter, req *http.Request, err error) {
rw.WriteHeader(http.StatusBadGateway)
rw.Write([]byte("Bad Gateway"))
_, _ = rw.Write([]byte("Bad Gateway: " + err.Error()))
},
}
}
-6
View File
@@ -3,7 +3,6 @@ package router
import (
"net/http"
"net/url"
"os"
"strconv"
"strings"
@@ -52,11 +51,6 @@ func Proxy() gin.HandlerFunc {
}
if !strings.HasPrefix(c.Request.URL.Path, "/api/v2/core") && (currentNode == "local" || len(currentNode) == 0) {
sockPath := "/etc/1panel/agent.sock"
if _, err := os.Stat(sockPath); err != nil {
helper.ErrorWithDetail(c, http.StatusBadRequest, "ErrProxy", err)
return
}
defer func() {
if err := recover(); err != nil && err != http.ErrAbortHandler {
global.LOG.Debug(err)
+3 -4
View File
@@ -10,13 +10,12 @@ import (
var store = base64Captcha.DefaultMemStore
func VerifyCode(codeID string, code string) string {
if codeID == "" {
return "ErrCaptchaCode"
}
vv := store.Get(codeID, true)
vv = strings.TrimSpace(vv)
code = strings.TrimSpace(code)
if codeID == "" || code == "" {
return "ErrCaptchaCode"
}
if strings.EqualFold(vv, code) {
return ""
}
+1 -1
View File
@@ -37,7 +37,7 @@
"codemirror": "^6.0.2",
"crypto-js": "^4.2.0",
"echarts": "^5.5.0",
"element-plus": "^2.11.2",
"element-plus": "2.11.9",
"fit2cloud-ui-plus": "^1.2.3",
"highlight.js": "^11.9.0",
"js-base64": "^3.7.7",
+9 -1
View File
@@ -39,9 +39,17 @@ export const commitContainer = (params: Container.ContainerCommit) => {
export const loadContainerInfo = (name: string) => {
return http.post<Container.ContainerHelper>(`/containers/info`, { name: name });
};
export const cleanComposeLog = (composeName: string, composePath: string, operateNode?: string) => {
const params = operateNode ? `?operateNode=${operateNode}` : '';
return http.post(
`/containers/compose/clean/log${params}`,
{ name: composeName, path: composePath },
TimeoutEnum.T_60S,
);
};
export const cleanContainerLog = (containerName: string, operateNode?: string) => {
const params = operateNode ? `?operateNode=${operateNode}` : '';
return http.post(`/containers/clean/log${params}`, { name: containerName });
return http.post(`/containers/clean/log${params}`, { name: containerName }, TimeoutEnum.T_60S);
};
export const containerItemStats = (containerID: string) => {
return http.post<Container.ContainerItemStats>(`/containers/item/stats`, { name: containerID }, TimeoutEnum.T_60S);
@@ -38,7 +38,7 @@
</template>
<script lang="ts" setup>
import { cleanContainerLog, DownloadFile } from '@/api/modules/container';
import { cleanComposeLog, cleanContainerLog, DownloadFile } from '@/api/modules/container';
import i18n from '@/lang';
import { dateFormatForName } from '@/utils/util';
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
@@ -48,6 +48,8 @@ import hightlight from '@/components/log/custom-hightlight/index.vue';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const em = defineEmits(['update:loading']);
const props = defineProps({
container: {
type: String,
@@ -93,6 +95,7 @@ const logSearch = reactive({
mode: 'all',
tail: props.defaultFollow ? 0 : 100,
compose: '',
resource: '',
});
const logHeight = 20;
const logCount = computed(() => logs.value.length);
@@ -215,6 +218,19 @@ const onClean = async () => {
if (props.node && props.node !== '') {
currentNode = props.node;
}
if (logSearch.compose !== '') {
em('update:loading', true);
await cleanComposeLog(logSearch.resource, logSearch.compose, currentNode)
.then(() => {
em('update:loading', false);
searchLogs();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.finally(() => {
em('update:loading', false);
});
return;
}
await cleanContainerLog(logSearch.container, currentNode);
searchLogs();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
@@ -240,6 +256,7 @@ const resizeObserver = ref<ResizeObserver | null>(null);
onMounted(() => {
logSearch.container = props.container;
logSearch.compose = props.compose;
logSearch.resource = props.resource;
logVisible.value = true;
logSearch.tail = 100;
@@ -110,6 +110,7 @@ const openEdit = (rowData: any): void => {
if (tensorRTLLM.value.exposedPorts == null) {
tensorRTLLM.value.exposedPorts = [];
}
tensorRTLLM.value.command = rowData.command.slice(1, -1).replace(/^'|'$/g, '').replace(/\\"/g, '"');
if (tensorRTLLM.value.extraHosts == null) {
tensorRTLLM.value.extraHosts = [];
}
+65 -28
View File
@@ -68,8 +68,8 @@
plain
round
size="small"
:disabled="!currentCompose?.workdir"
@click="openComposeFolder"
:disabled="!row?.workdir"
@click="openComposeFolder(row)"
>
{{ $t('home.dir') }}
</el-button>
@@ -97,13 +97,7 @@
>
{{ $t('commons.operate.restart') }}
</el-button>
<el-button
:disabled="row.createdBy !== '1Panel'"
plain
round
size="small"
@click="onDelete(row)"
>
<el-button plain round size="small" @click="onDelete(row)">
{{ $t('commons.operate.delete') }}
</el-button>
</div>
@@ -174,8 +168,7 @@
<el-descriptions-item :label="$t('container.memTotal')">
{{ computeSizeForDocker(row.memoryLimit) }}
</el-descriptions-item>
<el-descriptions-item>
<el-descriptions-item v-if="row.hasLoadSize">
<template #label>
{{ $t('container.sizeRw') }}
<el-tooltip :content="$t('container.sizeRwHelper')">
@@ -184,7 +177,10 @@
</template>
{{ computeSize2(row.sizeRw) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('container.sizeRootFs')">
<el-descriptions-item
:label="$t('container.sizeRootFs')"
v-if="row.hasLoadSize"
>
<template #label>
{{ $t('container.sizeRootFs') }}
<el-tooltip :content="$t('container.sizeRootFsHelper')">
@@ -194,6 +190,17 @@
{{ computeSize2(row.sizeRootFs) }}
</el-descriptions-item>
</el-descriptions>
<el-button
class="mt-2"
v-if="!row.hasLoadSize"
size="small"
link
type="primary"
@click="loadSize(row)"
>
{{ $t('container.loadSize') }}
</el-button>
</template>
</el-popover>
</div>
@@ -245,15 +252,17 @@
mode="yaml"
></CodemirrorPro>
</div>
<el-button type="primary" class="float-right" @click="onSubmitEdit">
<el-button type="primary" class="float-right mt-2" @click="onSubmitEdit">
{{ $t('commons.button.save') }}
</el-button>
</el-form>
<div v-show="showType === 'log'">
<ContainerLog
v-model:loading="detailLoading"
:key="currentCompose.path"
:compose="currentCompose.path"
:resource="currentCompose.name"
:highlightDiff="450"
:defaultFollow="true"
/>
@@ -282,7 +291,14 @@ import TerminalDialog from '@/views/container/container/terminal/index.vue';
import ContainerLogDialog from '@/components/log/container-drawer/index.vue';
import CreateDialog from '@/views/container/compose/create/index.vue';
import DeleteDialog from '@/views/container/compose/delete/index.vue';
import { composeOperator, composeUpdate, containerListStats, inspect, searchCompose } from '@/api/modules/container';
import {
composeOperator,
composeUpdate,
containerItemStats,
containerListStats,
inspect,
searchCompose,
} from '@/api/modules/container';
import DockerStatus from '@/views/container/docker-status/index.vue';
import i18n from '@/lang';
import { Container } from '@/api/interface/container';
@@ -381,11 +397,15 @@ const loadDetail = async (row: Container.ComposeInfo, withRefresh: boolean) => {
detailLoading.value = true;
currentCompose.value = row;
composeContainers.value = row.containers || [];
await inspect({ id: currentCompose.value.name, type: 'compose' }).then((res) => {
composeContent.value = res.data;
detailLoading.value = false;
});
loadContainerStats();
await inspect({ id: currentCompose.value.name, type: 'compose' })
.then((res) => {
composeContent.value = res.data;
detailLoading.value = false;
})
.finally(() => {
loadContainerStats();
detailLoading.value = false;
});
};
const loadContainerStats = async () => {
@@ -421,17 +441,20 @@ const handleComposeOperate = async (operation: 'up' | 'stop' | 'restart', row: a
loading.value = true;
const params = {
name: row.name,
path: currentCompose.value.path,
path: row.path,
operation: operation,
withFile: false,
force: false,
};
await composeOperator(params)
.then(() => {
.then(async () => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
if (row.name === currentCompose.value?.name) {
loadDetail(currentCompose.value, true);
await search();
if (currentCompose.value) {
const updated = data.value.find((item) => item.name === currentCompose.value.name);
if (updated) {
await loadDetail(updated, true);
}
}
})
.finally(() => {
@@ -440,6 +463,14 @@ const handleComposeOperate = async (operation: 'up' | 'stop' | 'restart', row: a
});
};
const loadSize = async (row: any) => {
containerItemStats(row.containerID).then((res) => {
row.sizeRw = res.data.sizeRw || 0;
row.sizeRootFs = res.data.sizeRootFs || 0;
row.hasLoadSize = true;
});
};
const onSubmitEdit = async () => {
const param = {
name: currentCompose.value.name,
@@ -452,16 +483,22 @@ const onSubmitEdit = async () => {
await composeUpdate(param)
.then(async () => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
await loadDetail(currentCompose.value, true);
await search();
if (currentCompose.value) {
const updated = data.value.find((item) => item.name === currentCompose.value.name);
if (updated) {
await loadDetail(updated, true);
}
}
})
.finally(() => {
loading.value = false;
});
};
const openComposeFolder = () => {
if (currentCompose.value?.workdir) {
routerToFileWithPath(currentCompose.value.workdir);
const openComposeFolder = (row: any) => {
if (row?.workdir) {
routerToFileWithPath(row.workdir);
}
};
const onInspectContainer = async (item: any) => {
@@ -206,6 +206,9 @@ const onOpenProcess = () => {
const onMessage = (message: any) => {
oldData.value = JSON.parse(message.data);
data.value = filterByStatus();
if (data.value == null) {
data.value = [];
}
loading.value = false;
};
const onerror = () => {};
@@ -269,6 +269,9 @@ const onMessage = (message: any) => {
isGetData.value = false;
oldData.value = JSON.parse(message.data);
data.value = filterByStatus();
if (data.value == null) {
data.value = [];
}
loading.value = false;
};
@@ -424,6 +424,9 @@ const getSetting = async () => {
globalStore.isFxplay = isFxplay.value;
globalStore.isOffLine = res.data.isOffLine;
globalStore.ignoreCaptcha = !res.data.needCaptcha;
if (!globalStore.ignoreCaptcha) {
loginVerify();
}
document.title = res.data.panelName;
i18n.warnHtmlMessage = false;
+8 -8
View File
@@ -16,7 +16,7 @@ import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import svgLoader from 'vite-svg-loader';
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
const prefix = `monaco-editor/esm/vs`;
const { dependencies, devDependencies, name, version } = pkg;
const __APP_INFO__ = {
@@ -97,9 +97,6 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
svgLoader({
defaultImport: 'url',
}),
monacoEditorPlugin({
languageWorkers: ['editorWorkerService', 'typescript', 'json', 'html', 'css'],
}),
],
esbuild: {
pure: viteEnv.VITE_DROP_CONSOLE ? ['console.log'] : [],
@@ -116,12 +113,15 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
manualChunks: {
jsonWorker: [`${prefix}/language/json/json.worker`],
cssWorker: [`${prefix}/language/css/css.worker`],
htmlWorker: [`${prefix}/language/html/html.worker`],
tsWorker: [`${prefix}/language/typescript/ts.worker`],
editorWorker: [`${prefix}/editor/editor.worker`],
},
},
},
},
optimizeDeps: {
include: ['monaco-editor/esm/vs/editor/editor.api'],
exclude: ['monaco-editor'],
},
};
});