mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-05-12 02:20:36 +08:00
* chore: ignore web/dist directory in git repository * 功能: 新增 SAP HANA 完整备份支持与 Backint 协议代理 - 修复 service 层校验 bug,使 SAP HANA 类型可正常创建 - 增强 hdbsql Runner:支持完整/增量/差异/日志备份、并行通道、失败重试 - 新增 Backint 协议代理(backupx backint 子命令),HANA 原生接口直连 BackupX 存储后端 - 新增本地 SQLite 目录维护 EBID↔对象键映射 - 前端新增 SAP HANA 扩展字段表单(备份类型/级别/通道数/重试次数/实例编号) - README 中英文补充 SAP HANA 两种模式的使用说明
103 lines
2.8 KiB
Go
103 lines
2.8 KiB
Go
package backint
|
||
|
||
import (
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/glebarez/sqlite"
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/clause"
|
||
gormlogger "gorm.io/gorm/logger"
|
||
)
|
||
|
||
// CatalogEntry 是 Backint 目录条目,建立 BID (备份 ID) 与对象键的映射。
|
||
//
|
||
// BID 是 Backint Agent 返回给 SAP HANA 的唯一标识,HANA 后续用它作为 RESTORE/DELETE
|
||
// 的句柄。Agent 用 catalog 查询该 BID 对应的实际存储对象键。
|
||
type CatalogEntry struct {
|
||
ID uint `gorm:"primaryKey"`
|
||
EBID string `gorm:"column:ebid;uniqueIndex;size:128;not null"`
|
||
ObjectKey string `gorm:"column:object_key;size:512;not null"`
|
||
SourcePath string `gorm:"column:source_path;size:1024"`
|
||
Size int64 `gorm:"column:size"`
|
||
CreatedAt time.Time `gorm:"column:created_at"`
|
||
}
|
||
|
||
// TableName 指定表名,避免 GORM 自动复数化。
|
||
func (CatalogEntry) TableName() string { return "backint_catalog" }
|
||
|
||
// Catalog 是本地 Backint 目录(SQLite 后端)。
|
||
type Catalog struct {
|
||
db *gorm.DB
|
||
}
|
||
|
||
// OpenCatalog 打开或创建 catalog 数据库。
|
||
func OpenCatalog(dbPath string) (*Catalog, error) {
|
||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
||
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
||
})
|
||
if err != nil {
|
||
return nil, fmt.Errorf("open catalog: %w", err)
|
||
}
|
||
if err := db.AutoMigrate(&CatalogEntry{}); err != nil {
|
||
return nil, fmt.Errorf("migrate catalog: %w", err)
|
||
}
|
||
return &Catalog{db: db}, nil
|
||
}
|
||
|
||
// Close 关闭底层连接。
|
||
func (c *Catalog) Close() error {
|
||
if c.db == nil {
|
||
return nil
|
||
}
|
||
sqlDB, err := c.db.DB()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return sqlDB.Close()
|
||
}
|
||
|
||
// Put 插入或更新一条记录。
|
||
func (c *Catalog) Put(entry CatalogEntry) error {
|
||
if entry.EBID == "" {
|
||
return fmt.Errorf("ebid is required")
|
||
}
|
||
if entry.CreatedAt.IsZero() {
|
||
entry.CreatedAt = time.Now().UTC()
|
||
}
|
||
// Upsert:EBID 冲突时更新 object_key/size/source_path
|
||
return c.db.Clauses(clause.OnConflict{
|
||
Columns: []clause.Column{{Name: "ebid"}},
|
||
DoUpdates: clause.AssignmentColumns([]string{
|
||
"object_key", "source_path", "size", "created_at",
|
||
}),
|
||
}).Create(&entry).Error
|
||
}
|
||
|
||
// Get 通过 EBID 查询条目。未找到返回 (nil, nil)。
|
||
func (c *Catalog) Get(ebid string) (*CatalogEntry, error) {
|
||
var entry CatalogEntry
|
||
err := c.db.Where("ebid = ?", ebid).First(&entry).Error
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil
|
||
}
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &entry, nil
|
||
}
|
||
|
||
// Delete 删除一条记录。
|
||
func (c *Catalog) Delete(ebid string) error {
|
||
return c.db.Where("ebid = ?", ebid).Delete(&CatalogEntry{}).Error
|
||
}
|
||
|
||
// List 列出全部条目。
|
||
func (c *Catalog) List() ([]CatalogEntry, error) {
|
||
var entries []CatalogEntry
|
||
if err := c.db.Order("created_at DESC").Find(&entries).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
return entries, nil
|
||
}
|