mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-12 10:55:51 +02:00
feat: backup and restore
This commit is contained in:
parent
60f35ef863
commit
4cb4695e7b
52 changed files with 9270 additions and 1439 deletions
290
internal/backup/backup_zip.go
Normal file
290
internal/backup/backup_zip.go
Normal file
|
@ -0,0 +1,290 @@
|
|||
package backup
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/uozi-tech/cosy"
|
||||
)
|
||||
|
||||
// createZipArchive creates a zip archive from a directory
|
||||
func createZipArchive(zipPath, srcDir string) error {
|
||||
// Create a new zip file
|
||||
zipFile, err := os.Create(zipPath)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipFile, err.Error())
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
// Create a new zip writer
|
||||
zipWriter := zip.NewWriter(zipFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
// Walk through all files in the source directory
|
||||
err = filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get relative path
|
||||
relPath, err := filepath.Rel(srcDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip if it's the source directory itself
|
||||
if relPath == "." {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if it's a symlink
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
// Get target of symlink
|
||||
linkTarget, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrReadSymlink, err.Error())
|
||||
}
|
||||
|
||||
// Create symlink entry in zip
|
||||
header := &zip.FileHeader{
|
||||
Name: relPath,
|
||||
Method: zip.Deflate,
|
||||
}
|
||||
header.SetMode(info.Mode())
|
||||
|
||||
writer, err := zipWriter.CreateHeader(header)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipEntry, err.Error())
|
||||
}
|
||||
|
||||
// Write link target as content (common way to store symlinks in zip)
|
||||
_, err = writer.Write([]byte(linkTarget))
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCopyContent, relPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create zip header
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipHeader, err.Error())
|
||||
}
|
||||
|
||||
// Set relative path as name
|
||||
header.Name = relPath
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
}
|
||||
|
||||
// Set compression method
|
||||
header.Method = zip.Deflate
|
||||
|
||||
// Create zip entry writer
|
||||
writer, err := zipWriter.CreateHeader(header)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipEntry, err.Error())
|
||||
}
|
||||
|
||||
// Skip if it's a directory
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Open source file
|
||||
source, err := os.Open(path)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrOpenSourceFile, err.Error())
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
// Copy to zip
|
||||
_, err = io.Copy(writer, source)
|
||||
return err
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// createZipArchiveFromFiles creates a zip archive from a list of files
|
||||
func createZipArchiveFromFiles(zipPath string, files []string) error {
|
||||
// Create a new zip file
|
||||
zipFile, err := os.Create(zipPath)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipFile, err.Error())
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
// Create a new zip writer
|
||||
zipWriter := zip.NewWriter(zipFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
// Add each file to the zip
|
||||
for _, file := range files {
|
||||
// Get file info
|
||||
info, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrOpenSourceFile, err.Error())
|
||||
}
|
||||
|
||||
// Create zip header
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipHeader, err.Error())
|
||||
}
|
||||
|
||||
// Set base name as header name
|
||||
header.Name = filepath.Base(file)
|
||||
|
||||
// Set compression method
|
||||
header.Method = zip.Deflate
|
||||
|
||||
// Create zip entry writer
|
||||
writer, err := zipWriter.CreateHeader(header)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipEntry, err.Error())
|
||||
}
|
||||
|
||||
// Open source file
|
||||
source, err := os.Open(file)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrOpenSourceFile, err.Error())
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
// Copy to zip
|
||||
_, err = io.Copy(writer, source)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCopyContent, file)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculateFileHash calculates the SHA-256 hash of a file
|
||||
func calculateFileHash(filePath string) (string, error) {
|
||||
// Open file
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return "", cosy.WrapErrorWithParams(ErrReadFile, filePath)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Create hash
|
||||
hash := sha256.New()
|
||||
if _, err := io.Copy(hash, file); err != nil {
|
||||
return "", cosy.WrapErrorWithParams(ErrCalculateHash, err.Error())
|
||||
}
|
||||
|
||||
// Return hex hash
|
||||
return hex.EncodeToString(hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// createZipArchiveToBuffer creates a zip archive of files in the specified directory
|
||||
// and writes the zip content to the provided buffer
|
||||
func createZipArchiveToBuffer(buffer *bytes.Buffer, sourceDir string) error {
|
||||
// Create a zip writer that writes to the buffer
|
||||
zipWriter := zip.NewWriter(buffer)
|
||||
defer zipWriter.Close()
|
||||
|
||||
// Walk through all files in the source directory
|
||||
err := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip the source directory itself
|
||||
if path == sourceDir {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the relative path to the source directory
|
||||
relPath, err := filepath.Rel(sourceDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if it's a symlink
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
// Get target of symlink
|
||||
linkTarget, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrReadSymlink, err.Error())
|
||||
}
|
||||
|
||||
// Create symlink entry in zip
|
||||
header := &zip.FileHeader{
|
||||
Name: relPath,
|
||||
Method: zip.Deflate,
|
||||
}
|
||||
header.SetMode(info.Mode())
|
||||
|
||||
writer, err := zipWriter.CreateHeader(header)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipEntry, err.Error())
|
||||
}
|
||||
|
||||
// Write link target as content
|
||||
_, err = writer.Write([]byte(linkTarget))
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCopyContent, relPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a zip header from the file info
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipHeader, err.Error())
|
||||
}
|
||||
|
||||
// Set the name to be relative to the source directory
|
||||
header.Name = relPath
|
||||
|
||||
// Set the compression method
|
||||
if !info.IsDir() {
|
||||
header.Method = zip.Deflate
|
||||
}
|
||||
|
||||
// Create the entry in the zip file
|
||||
writer, err := zipWriter.CreateHeader(header)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCreateZipEntry, err.Error())
|
||||
}
|
||||
|
||||
// If it's a directory, we're done
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Open the source file
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrOpenSourceFile, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Copy the file contents to the zip entry
|
||||
_, err = io.Copy(writer, file)
|
||||
if err != nil {
|
||||
return cosy.WrapErrorWithParams(ErrCopyContent, relPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Close the zip writer to ensure all data is written
|
||||
return zipWriter.Close()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue