2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 06:47:20 +08:00

refactor: 重构io包

This commit is contained in:
耗子
2024-06-23 00:59:07 +08:00
parent 6bd07251fc
commit 85c38e5bc5
35 changed files with 368 additions and 668 deletions

View File

@@ -1,255 +0,0 @@
package tools
import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"os/user"
"path/filepath"
"strings"
"github.com/goravel/framework/support/env"
"github.com/mholt/archiver/v3"
"github.com/spf13/cast"
)
// Write 写入文件
func Write(path string, data string, permission os.FileMode) error {
if err := os.MkdirAll(filepath.Dir(path), permission); err != nil {
return err
}
err := os.WriteFile(path, []byte(data), permission)
if err != nil {
return err
}
return nil
}
// WriteAppend 追加写入文件
func WriteAppend(path string, data string) error {
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(data)
if err != nil {
return err
}
return nil
}
// Read 读取文件
func Read(path string) (string, error) {
data, err := os.ReadFile(path)
return string(data), err
}
// Remove 删除文件/目录
func Remove(path string) error {
return os.RemoveAll(path)
}
// Mkdir 创建目录
func Mkdir(path string, permission os.FileMode) error {
return os.MkdirAll(path, permission)
}
// Chmod 修改文件/目录权限
func Chmod(path string, permission os.FileMode) error {
if env.IsWindows() {
return errors.New("chmod is not supported on Windows")
}
cmd := exec.Command("chmod", "-R", fmt.Sprintf("%o", permission), path)
return cmd.Run()
}
// Chown 修改文件或目录所有者
func Chown(path, user, group string) error {
if env.IsWindows() {
return errors.New("chown is not supported on Windows")
}
cmd := exec.Command("chown", "-R", user+":"+group, path)
return cmd.Run()
}
// Exists 判断路径是否存在
func Exists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
// Empty 判断路径是否为空
func Empty(path string) bool {
files, err := os.ReadDir(path)
if err != nil {
return true
}
return len(files) == 0
}
func Mv(src, dst string) error {
err := os.Rename(src, dst)
if err != nil {
// 如果在不同的文件系统中移动文件os.Rename 可能会失败
err = Cp(src, dst)
if err != nil {
return err
}
err = os.RemoveAll(src)
}
return err
}
// Cp 复制文件或目录
func Cp(src, dst string) error {
srcInfo, err := os.Stat(src)
if err != nil {
return err
}
if srcInfo.IsDir() {
return copyDir(src, dst)
}
return copyFile(src, dst)
}
func copyFile(src, dst string) error {
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
_, err = io.Copy(dstFile, srcFile)
return err
}
func copyDir(src, dst string) error {
srcInfo, err := os.Stat(src)
if err != nil {
return err
}
err = os.MkdirAll(dst, srcInfo.Mode())
if err != nil {
return err
}
entries, err := os.ReadDir(src)
if err != nil {
return err
}
for _, entry := range entries {
srcPath := filepath.Join(src, entry.Name())
dstPath := filepath.Join(dst, entry.Name())
if entry.IsDir() {
err = copyDir(srcPath, dstPath)
if err != nil {
return err
}
} else {
err = copyFile(srcPath, dstPath)
if err != nil {
return err
}
}
}
return nil
}
// Size 获取路径大小
func Size(path string) (int64, error) {
var size int64
err := filepath.Walk(path, func(filePath string, info os.FileInfo, walkErr error) error {
if walkErr != nil {
return walkErr
}
size += info.Size()
return nil
})
return size, err
}
// FileInfo 获取文件大小
func FileInfo(path string) (os.FileInfo, error) {
return os.Stat(path)
}
// UnArchive 智能解压文件
func UnArchive(file string, dst string) error {
return archiver.Unarchive(file, dst)
}
// Archive 智能压缩文件
func Archive(src []string, dst string) error {
return archiver.Archive(src, dst)
}
// TempDir 创建临时目录
func TempDir(prefix string) (string, error) {
return os.MkdirTemp("", prefix)
}
// TempFile 创建临时文件
func TempFile(prefix string) (*os.File, error) {
return os.CreateTemp("", prefix)
}
// IsSymlink 判读是否为软链接
func IsSymlink(mode os.FileMode) bool {
return mode&os.ModeSymlink != 0
}
// IsHidden 判断是否为隐藏文件
func IsHidden(path string) bool {
_, file := filepath.Split(path)
return strings.HasPrefix(file, ".")
}
// GetSymlink 获取软链接目标
func GetSymlink(path string) string {
linkPath, err := os.Readlink(path)
if err != nil {
return ""
}
return linkPath
}
// GetUser 通过 uid 获取用户名
func GetUser(uid uint32) string {
usr, err := user.LookupId(cast.ToString(uid))
if err != nil {
return ""
}
return usr.Username
}
// GetGroup 通过 gid 获取组名
func GetGroup(gid uint32) string {
usr, err := user.LookupGroupId(cast.ToString(gid))
if err != nil {
return ""
}
return usr.Name
}

View File

@@ -1,330 +0,0 @@
package tools
import (
"os/user"
"path/filepath"
"testing"
"time"
"github.com/goravel/framework/support/env"
"github.com/stretchr/testify/suite"
)
type SystemHelperTestSuite struct {
suite.Suite
}
func TestSystemHelperTestSuite(t *testing.T) {
suite.Run(t, &SystemHelperTestSuite{})
}
func (s *SystemHelperTestSuite) WriteCreatesFileWithCorrectContent() {
filePath, _ := TempFile("testfile")
err := Write(filePath.Name(), "test data", 0644)
s.Nil(err)
content, _ := Read(filePath.Name())
s.Equal("test data", content)
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) WriteCreatesDirectoriesIfNeeded() {
filePath, _ := TempFile("testdir/testfile")
err := Write(filePath.Name(), "test data", 0644)
s.Nil(err)
content, _ := Read(filePath.Name())
s.Equal("test data", content)
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) WriteFailsIfDirectoryCannotBeCreated() {
filePath := "/nonexistent/testfile"
err := Write(filePath, "test data", 0644)
s.NotNil(err)
}
func (s *SystemHelperTestSuite) WriteFailsIfFileCannotBeWritten() {
filePath, _ := TempFile("testfile")
s.Nil(filePath.Close())
s.Nil(Chmod(filePath.Name(), 0400))
err := Write(filePath.Name(), "test data", 0644)
s.NotNil(err)
s.Nil(Chmod(filePath.Name(), 0644))
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) WriteAppendSuccessfullyAppendsDataToFile() {
filePath, _ := TempFile("testfile")
err := Write(filePath.Name(), "initial data", 0644)
s.Nil(err)
err = WriteAppend(filePath.Name(), " appended data")
s.Nil(err)
content, _ := Read(filePath.Name())
s.Equal("initial data appended data", content)
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) WriteAppendCreatesFileIfNotExists() {
filePath, _ := TempFile("testfile")
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
err := WriteAppend(filePath.Name(), "test data")
s.Nil(err)
content, _ := Read(filePath.Name())
s.Equal("test data", content)
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) WriteAppendReturnsErrorIfPathIsADirectory() {
dirPath, _ := TempDir("testdir")
err := WriteAppend(dirPath, "test data")
s.NotNil(err)
s.Nil(Remove(dirPath))
}
func (s *SystemHelperTestSuite) ReadSuccessfullyReadsFileContent() {
filePath, _ := TempFile("testfile")
err := Write(filePath.Name(), "test data", 0644)
s.Nil(err)
content, err := Read(filePath.Name())
s.Nil(err)
s.Equal("test data", content)
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) ReadReturnsErrorForNonExistentFile() {
_, err := Read("/nonexistent/testfile")
s.NotNil(err)
}
func (s *SystemHelperTestSuite) RemoveSuccessfullyRemovesFile() {
filePath, _ := TempFile("testfile")
err := Write(filePath.Name(), "test data", 0644)
s.Nil(err)
err = Remove(filePath.Name())
s.Nil(err)
s.False(Exists(filePath.Name()))
}
func (s *SystemHelperTestSuite) RemoveReturnsErrorForNonExistentFile() {
err := Remove("/nonexistent/testfile")
s.NotNil(err)
}
func (s *SystemHelperTestSuite) TestExec() {
output, err := Exec("echo test")
s.Equal("test", output)
s.Nil(err)
}
func (s *SystemHelperTestSuite) TestExecAsync() {
command := "echo test > test.txt"
if env.IsWindows() {
command = "echo test> test.txt"
}
err := ExecAsync(command)
s.Nil(err)
time.Sleep(time.Second)
content, err := Read("test.txt")
s.Nil(err)
condition := "test\n"
if env.IsWindows() {
condition = "test\r\n"
}
s.Equal(condition, content)
s.Nil(Remove("test.txt"))
}
func (s *SystemHelperTestSuite) TestMkdir() {
dirPath, _ := TempDir("testdir")
s.Nil(Mkdir(dirPath, 0755))
s.Nil(Remove(dirPath))
}
func (s *SystemHelperTestSuite) TestChmod() {
filePath, _ := TempFile("testfile")
err := Write(filePath.Name(), "test data", 0644)
s.Nil(err)
s.Nil(filePath.Close())
s.Nil(Chmod(filePath.Name(), 0755))
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) TestChown() {
filePath, _ := TempFile("testfile")
err := Write(filePath.Name(), "test data", 0644)
s.Nil(err)
currentUser, err := user.Current()
s.Nil(err)
groups, err := currentUser.GroupIds()
s.Nil(err)
err = Chown(filePath.Name(), currentUser.Username, groups[0])
if env.IsWindows() {
s.NotNil(err)
} else {
s.Nil(err)
}
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) TestExists() {
filePath, _ := TempFile("testfile")
s.True(Exists(filePath.Name()))
s.False(Exists("123"))
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) TestEmpty() {
filePath, _ := TempFile("testfile")
s.True(Empty(filePath.Name()))
if env.IsWindows() {
s.True(Empty("C:\\Windows\\System32\\drivers\\etc\\hosts"))
} else {
s.True(Empty("/etc/hosts"))
}
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) TestMv() {
filePath, _ := TempFile("testfile")
err := Write(filePath.Name(), "test data", 0644)
s.Nil(err)
newFilePath, _ := TempFile("testfile2")
s.Nil(newFilePath.Close())
s.Nil(filePath.Close())
s.Nil(Mv(filePath.Name(), newFilePath.Name()))
s.False(Exists(filePath.Name()))
s.Nil(Remove(newFilePath.Name()))
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) TestCp() {
tempDir, _ := TempDir("testdir")
err := Write(filepath.Join(tempDir, "testfile"), "test data", 0644)
s.Nil(err)
s.Nil(Cp(filepath.Join(tempDir, "testfile"), filepath.Join(tempDir, "testfile2")))
s.True(Exists(filepath.Join(tempDir, "testfile2")))
s.Nil(Remove(tempDir))
}
func (s *SystemHelperTestSuite) TestSize() {
filePath, _ := TempFile("testfile")
err := Write(filePath.Name(), "test data", 0644)
s.Nil(err)
size, err := Size(filePath.Name())
s.Nil(err)
s.Equal(int64(len("test data")), size)
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) TestFileInfo() {
filePath, _ := TempFile("testfile")
err := Write(filePath.Name(), "test data", 0644)
s.Nil(err)
info, err := FileInfo(filePath.Name())
s.Nil(err)
s.Equal(filepath.Base(filePath.Name()), info.Name())
s.Nil(filePath.Close())
s.Nil(Remove(filePath.Name()))
}
func (s *SystemHelperTestSuite) TestUnArchiveSuccessfullyUnarchivesFile() {
file, _ := TempFile("test")
dstDir, _ := TempDir("archive")
err := Write(file.Name(), "test data", 0644)
s.Nil(err)
err = Archive([]string{file.Name()}, filepath.Join(dstDir, "test.zip"))
s.Nil(err)
s.FileExists(filepath.Join(dstDir, "test.zip"))
err = UnArchive(filepath.Join(dstDir, "test.zip"), dstDir)
s.Nil(err)
s.FileExists(filepath.Join(dstDir, filepath.Base(file.Name())))
s.Nil(file.Close())
s.Nil(Remove(file.Name()))
s.Nil(Remove(dstDir))
}
func (s *SystemHelperTestSuite) TestUnArchiveFailsForNonExistentFile() {
srcFile := "nonexistent.zip"
dstDir, _ := TempDir("unarchived")
err := UnArchive(srcFile, dstDir)
s.NotNil(err)
s.Nil(Remove(dstDir))
}
func (s *SystemHelperTestSuite) TestArchiveSuccessfullyArchivesFiles() {
srcFile, _ := TempFile("test")
dstDir, _ := TempDir("archive")
err := Write(srcFile.Name(), "test data", 0644)
s.Nil(err)
err = Archive([]string{srcFile.Name()}, filepath.Join(dstDir, "test.zip"))
s.Nil(err)
s.FileExists(filepath.Join(dstDir, "test.zip"))
s.Nil(srcFile.Close())
s.Nil(Remove(srcFile.Name()))
s.Nil(Remove(dstDir))
}
func (s *SystemHelperTestSuite) TestArchiveFailsForNonExistentFiles() {
srcFile := "nonexistent"
dstDir, _ := TempDir("archive")
err := Archive([]string{srcFile}, filepath.Join(dstDir, "test.zip"))
s.NotNil(err)
s.Nil(Remove(dstDir))
}

View File

@@ -20,6 +20,7 @@ import (
"github.com/shirou/gopsutil/net"
"github.com/spf13/cast"
"github.com/TheTNB/panel/pkg/io"
"github.com/TheTNB/panel/pkg/shell"
)
@@ -393,13 +394,13 @@ func UpdatePanel(panelInfo PanelInfo) error {
color.Green().Printfln("下载链接: " + panelInfo.DownloadUrl)
color.Green().Printfln("前置检查...")
if Exists("/tmp/panel-storage.zip") || Exists("/tmp/panel.conf.bak") {
if io.Exists("/tmp/panel-storage.zip") || io.Exists("/tmp/panel.conf.bak") {
return errors.New("检测到 /tmp 存在临时文件,可能是上次更新失败导致的,请谨慎排除后重试")
}
color.Green().Printfln("备份面板数据...")
// 备份面板
if err := Archive([]string{"/www/panel"}, "/www/backup/panel/panel-"+carbon.Now().ToShortDateTimeString()+".zip"); err != nil {
if err := io.Archive([]string{"/www/panel"}, "/www/backup/panel/panel-"+carbon.Now().ToShortDateTimeString()+".zip"); err != nil {
color.Red().Printfln("备份面板失败")
return err
}
@@ -411,7 +412,7 @@ func UpdatePanel(panelInfo PanelInfo) error {
color.Red().Printfln("备份面板配置失败")
return err
}
if !Exists("/tmp/panel-storage.zip") || !Exists("/tmp/panel.conf.bak") {
if !io.Exists("/tmp/panel-storage.zip") || !io.Exists("/tmp/panel.conf.bak") {
return errors.New("备份面板数据失败")
}
color.Green().Printfln("备份完成")
@@ -432,7 +433,7 @@ func UpdatePanel(panelInfo PanelInfo) error {
color.Red().Printfln("下载失败")
return err
}
if !Exists("/www/panel/"+panelInfo.DownloadName) || !Exists("/www/panel/"+panelInfo.Checksums) {
if !io.Exists("/www/panel/"+panelInfo.DownloadName) || !io.Exists("/www/panel/"+panelInfo.Checksums) {
return errors.New("下载失败")
}
color.Green().Printfln("下载完成")
@@ -442,7 +443,7 @@ func UpdatePanel(panelInfo PanelInfo) error {
if check != panelInfo.DownloadName+": OK" || err != nil {
return errors.New("下载文件校验失败")
}
if err = Remove("/www/panel/" + panelInfo.Checksums); err != nil {
if err = io.Remove("/www/panel/" + panelInfo.Checksums); err != nil {
color.Red().Printfln("清理临时文件失败")
return err
}
@@ -453,7 +454,7 @@ func UpdatePanel(panelInfo PanelInfo) error {
color.Red().Printfln("更新失败")
return err
}
if !Exists("/www/panel/panel") {
if !io.Exists("/www/panel/panel") {
return errors.New("更新失败,可能是下载过程中出现了问题")
}
color.Green().Printfln("更新完成")
@@ -471,7 +472,7 @@ func UpdatePanel(panelInfo PanelInfo) error {
color.Red().Printfln("恢复面板脚本失败")
return err
}
if !Exists("/www/panel/storage/panel.db") || !Exists("/www/panel/panel.conf") {
if !io.Exists("/www/panel/storage/panel.db") || !io.Exists("/www/panel/panel.conf") {
return errors.New("恢复面板数据失败")
}
color.Green().Printfln("恢复完成")

24
pkg/tools/user.go Normal file
View File

@@ -0,0 +1,24 @@
package tools
import (
"github.com/spf13/cast"
"os/user"
)
// GetUser 通过 uid 获取用户名
func GetUser(uid uint32) string {
usr, err := user.LookupId(cast.ToString(uid))
if err != nil {
return ""
}
return usr.Username
}
// GetGroup 通过 gid 获取组名
func GetGroup(gid uint32) string {
usr, err := user.LookupGroupId(cast.ToString(gid))
if err != nil {
return ""
}
return usr.Name
}