fix: Fix SSH log export exception when logs are empty (#10114)

This commit is contained in:
ssongliu 2025-08-22 22:37:52 +08:00 committed by GitHub
parent 2e06b78409
commit 99b04e3635
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 103 additions and 34 deletions

View file

@ -2,10 +2,6 @@ package v2
import (
"encoding/base64"
"net/http"
"net/url"
"os"
"strconv"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
@ -231,7 +227,7 @@ func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
// @Summary Export host SSH logs
// @Accept json
// @Param request body dto.SearchSSHLog true "request"
// @Success 200 {object} dto.PageResult
// @Success 200 {string} path
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/log/export [post]
@ -245,19 +241,7 @@ func (b *BaseApi) ExportSSHLogs(c *gin.Context) {
helper.InternalServer(c, err)
return
}
file, err := os.Open(tmpFile)
if err != nil {
helper.InternalServer(c, err)
return
}
defer func() {
_ = file.Close()
_ = os.RemoveAll(tmpFile)
}()
info, _ := file.Stat()
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
helper.SuccessWithData(c, tmpFile)
}
// @Tags SSH

View file

@ -478,6 +478,9 @@ func (u *SSHService) ExportLog(ctx *gin.Context, req dto.SearchSSHLog) (string,
if err != nil {
return "", err
}
if len(logs) == 0 {
return "", buserr.New("ErrRecordNotFound")
}
tmpFileName := path.Join(global.Dir.TmpDir, "export/ssh-log", fmt.Sprintf("1panel-ssh-log-%s.csv", time.Now().Format(constant.DateTimeSlimLayout)))
if _, err := os.Stat(path.Dir(tmpFileName)); err != nil {
_ = os.MkdirAll(path.Dir(tmpFileName), constant.DirPerm)

View file

@ -93,6 +93,15 @@ ErrAppVersionDeprecated: "The {{ .name }} application is not compatible with the
ErrDockerFailed: "The state of Docker is abnormal, please check the service status"
ErrDockerComposeCmdNotFound: "The Docker Compose command does not exist, please install this command on the host machine first"
#ssh
ExportIP: "Login IP"
ExportArea: "Location"
ExportPort: "Port"
ExportAuthMode: "Login Method"
ExportUser: "User"
ExportStatus: "Login Status"
ExportDate: "Time"
#file
ErrFileCanNotRead: 'This file does not support preview'
ErrFileToLarge: 'The file is larger than 10M and cannot be opened'

View file

@ -93,6 +93,15 @@ ErrAppVersionDeprecated: "{{ .name }} アプリケーションは現在の 1Pane
ErrDockerFailed: "Docker の状態が異常です。サービス状態を確認してください"
ErrDockerComposeCmdNotFound: "Docker Compose コマンドは存在しません。ホストマシンにこのコマンドを先にインストールしてください"
#ssh
ExportIP: "ログインIP"
ExportArea: "地域"
ExportPort: "ポート"
ExportAuthMode: "ログイン方式"
ExportUser: "ユーザー"
ExportStatus: "ログイン状態"
ExportDate: "時間"
#file
ErrFileCanNotRead: 'このファイルはプレビューをサポートしていません'
ErrFileToLarge: 'ファイルは 10M より大きいため開けません'

View file

@ -93,6 +93,15 @@ ErrAppVersionDeprecated: "{{ .name }} 응용 프로그램은 현재 1Panel 버
ErrDockerFailed: "Docker의 상태가 비정상입니다. 서비스 상태를 확인하세요"
ErrDockerComposeCmdNotFound: "Docker Compose 명령이 없습니다. 호스트 머신에 먼저 이 명령을 설치하세요"
#ssh
ExportIP: "로그인 IP"
ExportArea: "위치"
ExportPort: "포트"
ExportAuthMode: "로그인 방식"
ExportUser: "사용자"
ExportStatus: "로그인 상태"
ExportDate: "시간"
#파일
ErrFileCanNotRead: '이 파일은 미리보기를 지원하지 않습니다'
ErrFileToLarge: '파일이 10M보다 커서 열 수 없습니다'

View file

@ -96,6 +96,15 @@ ErrAppVersionDeprecated: "Aplikasi {{ .name }} tidak sesuai dengan versi 1Panel
ErrDockerFailed: "Keadaan Docker tidak normal, sila periksa status perkhidmatan"
ErrDockerComposeCmdNotFound: "Perintah Docker Compose tidak wujud, sila pasang perintah ini di mesin tuan terlebih dahulu"
#ssh
ExportIP: "IP Log Masuk"
ExportArea: "Lokasi"
ExportPort: "Port"
ExportAuthMode: "Kaedah Log Masuk"
ExportUser: "Pengguna"
ExportStatus: "Status Log Masuk"
ExportDate: "Masa"
#fail
ErrFileCanNotRead: 'Fail ini tidak menyokong pratonton'
ErrFileToLarge: 'Fail lebih besar daripada 10M dan tidak boleh dibuka'

View file

@ -96,6 +96,15 @@ ErrAppVersionDeprecated: "O aplicativo {{ .name }} não é compatível com a ver
ErrDockerFailed: "O estado do Docker está anormal, verifique o status do serviço"
ErrDockerComposeCmdNotFound: "O comando Docker Compose não existe, por favor, instale este comando na máquina host primeiro"
#ssh
ExportIP: "IP de Login"
ExportArea: "Localização"
ExportPort: "Porta"
ExportAuthMode: "Método de Login"
ExportUser: "Usuário"
ExportStatus: "Status de Login"
ExportDate: "Hora"
#arquivo
ErrFileCanNotRead: 'Este arquivo não suporta visualização'
ErrFileToLarge: 'O arquivo é maior que 10M e não pode ser aberto'

View file

@ -96,6 +96,15 @@ ErrAppVersionDeprecated: "Приложение {{ .name }} несовмести
ErrDockerFailed: "Состояние Docker аномально, проверьте состояние сервиса"
ErrDockerComposeCmdNotFound: "Команда Docker Compose отсутствует, пожалуйста, установите эту команду на хост-машине сначала"
#ssh
ExportIP: "IP входа"
ExportArea: "Местоположение"
ExportPort: "Порт"
ExportAuthMode: "Способ входа"
ExportUser: "Пользователь"
ExportStatus: "Статус входа"
ExportDate: "Время"
#файл
ErrFileCanNotRead: 'Этот файл не поддерживает предварительный просмотр'
ErrFileToLarge: 'Файл больше 10 МБ и не может быть открыт'

View file

@ -96,6 +96,15 @@ ErrAppVersionDeprecated: "{{ .name }} uygulaması mevcut 1Panel sürümü ile uy
ErrDockerFailed: "Docker durumu anormal, lütfen servis durumunu kontrol edin"
ErrDockerComposeCmdNotFound: "Docker Compose komutu mevcut değil, lütfen önce bu komutu host makinesine yükleyin"
#ssh
ExportIP: "Giriş IP"
ExportArea: "Konum"
ExportPort: "Port"
ExportAuthMode: "Giriş Yöntemi"
ExportUser: "Kullanıcı"
ExportStatus: "Giriş Durumu"
ExportDate: "Zaman"
#file
ErrFileCanNotRead: 'Bu dosya önizlemeyi desteklemiyor'
ErrFileToLarge: 'Dosya 10M'dan büyük ve açılamıyor'

View file

@ -92,6 +92,15 @@ ErrAppVersionDeprecated: "{{ .name }} 應用不適配目前 1Panel 版本,跳
ErrDockerFailed: "Docker 狀態異常,請檢查服務狀態"
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,請先在宿主機安裝此命令"
#ssh
ExportIP: "登入IP"
ExportArea: "歸屬地"
ExportPort: "端口"
ExportAuthMode: "登入方式"
ExportUser: "使用者"
ExportStatus: "登入狀態"
ExportDate: "時間"
#file
ErrFileCanNotRead: '此檔案不支援預覽'
ErrFileToLarge: '檔案超過10M無法開啟'

View file

@ -92,6 +92,15 @@ ErrAppVersionDeprecated: " {{ .name }} 应用不适配当前 1Panel 版本,跳
ErrDockerFailed: "Docker 状态异常,请检查服务状态"
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,请先在宿主机安装此命令"
#ssh
ExportIP: "登录 IP"
ExportArea: "归属地"
ExportPort: "端口"
ExportAuthMode: "登录方式"
ExportUser: "用户"
ExportStatus: "登录状态"
ExportDate: "时间"
#file
ErrFileCanNotRead: "此文件不支持预览"
ErrFileToLarge: "文件超过10M无法打开"

View file

@ -6,6 +6,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/i18n"
)
func ExportSSHLogs(filename string, logs []dto.SSHHistory) error {
@ -18,7 +19,15 @@ func ExportSSHLogs(filename string, logs []dto.SSHHistory) error {
writer := csv.NewWriter(file)
defer writer.Flush()
if err := writer.Write([]string{"IP", "Area", "Port", "AuthMode", "User", "Status", "Date"}); err != nil {
if err := writer.Write([]string{
i18n.GetMsgByKey("ExportIP"),
i18n.GetMsgByKey("ExportArea"),
i18n.GetMsgByKey("ExportPort"),
i18n.GetMsgByKey("ExportAuthMode"),
i18n.GetMsgByKey("ExportUser"),
i18n.GetMsgByKey("ExportStatus"),
i18n.GetMsgByKey("ExportDate"),
}); err != nil {
return err
}

View file

@ -99,8 +99,5 @@ export const loadSSHLogs = (params: Host.searchSSHLog) => {
return http.post<ResPage<Host.sshHistory>>(`/hosts/ssh/log`, params);
};
export const exportSSHLogs = (params: Host.searchSSHLog) => {
return http.download<BlobPart>('/hosts/ssh/log/export', params, {
responseType: 'blob',
timeout: TimeoutEnum.T_40S,
});
return http.post<string>('/hosts/ssh/log/export', params, TimeoutEnum.T_40S);
};

View file

@ -89,11 +89,11 @@
</template>
<script setup lang="ts">
import { dateFormat, getDateStr } from '@/utils/util';
import { dateFormat, downloadFile } from '@/utils/util';
import { onMounted, reactive, ref } from 'vue';
import { exportSSHLogs, loadSSHLogs } from '@/api/modules/host';
import { MsgSuccess } from '@/utils/message';
import i18n from '@/lang';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const loading = ref();
const data = ref();
@ -146,14 +146,9 @@ const onSubmitExport = async () => {
};
await exportSSHLogs(params)
.then((res) => {
const downloadUrl = window.URL.createObjectURL(new Blob([res]));
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
a.download = '1panel-ssh-log-' + getDateStr() + '.csv';
const event = new MouseEvent('click');
a.dispatchEvent(event);
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
if (res.data) {
downloadFile(res.data, globalStore.currentNode);
}
open.value = false;
})
.catch(() => {