发布 0.1.0:通知渠道、告警规则、令牌版本与安全加固
This commit is contained in:
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -15,10 +16,12 @@ import (
|
||||
|
||||
// OciConfigService 管理 API Key 配置的导入、测活与快照同步。
|
||||
type OciConfigService struct {
|
||||
db *gorm.DB
|
||||
cipher *crypto.Cipher
|
||||
client oci.Client
|
||||
// auditRaw 暂存审计原始事件(eventId → raw,TTL 10 分钟),
|
||||
db *gorm.DB
|
||||
cipher *crypto.Cipher
|
||||
client oci.Client
|
||||
cleanupTasks *TaskService
|
||||
cleanupEvents *LogEventService
|
||||
// auditRaw 按 configId:eventId 暂存审计原始事件(TTL 10 分钟),
|
||||
// 列表响应剥离 raw 后详情接口据此秒开;miss 走小窗重查兜底。
|
||||
auditRaw *cache.Cache
|
||||
}
|
||||
@@ -28,6 +31,12 @@ func NewOciConfigService(db *gorm.DB, cipher *crypto.Cipher, client oci.Client)
|
||||
return &OciConfigService{db: db, cipher: cipher, client: client, auditRaw: cache.New(auditRawMax)}
|
||||
}
|
||||
|
||||
// SetTenantCleanupDeps 注入租户删除提交后的任务与告警内存状态同步依赖。
|
||||
func (s *OciConfigService) SetTenantCleanupDeps(tasks *TaskService, events *LogEventService) {
|
||||
s.cleanupTasks = tasks
|
||||
s.cleanupEvents = events
|
||||
}
|
||||
|
||||
// ImportInput 是导入一份 API Key 的输入:
|
||||
// ConfigINI 与显式字段二选一(ConfigINI 优先),私钥内容必填。
|
||||
type ImportInput struct {
|
||||
@@ -263,17 +272,18 @@ func (s *OciConfigService) Get(ctx context.Context, id uint) (*model.OciConfig,
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// Delete 删除配置及其区域 / 区间缓存。
|
||||
// Delete 在单一事务中删除配置及全部租户级本地关联数据。
|
||||
func (s *OciConfigService) Delete(ctx context.Context, id uint) error {
|
||||
res := s.db.WithContext(ctx).Delete(&model.OciConfig{}, id)
|
||||
if res.Error != nil {
|
||||
return fmt.Errorf("delete oci config %d: %w", id, res.Error)
|
||||
unlock := func() {}
|
||||
if s.cleanupTasks != nil {
|
||||
unlock = s.cleanupTasks.lockTenantCleanup()
|
||||
}
|
||||
if res.RowsAffected == 0 {
|
||||
return fmt.Errorf("delete oci config %d: %w", id, gorm.ErrRecordNotFound)
|
||||
defer unlock()
|
||||
result, err := s.deleteTenant(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.db.WithContext(ctx).Where("oci_config_id = ?", id).Delete(&model.RegionCache{})
|
||||
s.db.WithContext(ctx).Where("oci_config_id = ?", id).Delete(&model.CompartmentCache{})
|
||||
s.afterTenantDelete(ctx, result)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -294,12 +304,43 @@ func (s *OciConfigService) refresh(ctx context.Context, cfg *model.OciConfig, cr
|
||||
s.applyProfile(ctx, cfg, cred)
|
||||
s.syncScopeCaches(ctx, cfg, cred)
|
||||
}
|
||||
if err := s.db.WithContext(ctx).Save(cfg).Error; err != nil {
|
||||
return fmt.Errorf("save oci config snapshot: %w", err)
|
||||
s.applySuspension(ctx, cfg, cred)
|
||||
return s.saveSnapshot(ctx, cfg)
|
||||
}
|
||||
|
||||
// saveSnapshot 只更新当前版本,避免在途测活把已删租户重新插入。
|
||||
func (s *OciConfigService) saveSnapshot(ctx context.Context, cfg *model.OciConfig) error {
|
||||
res := s.db.WithContext(ctx).Model(&model.OciConfig{}).
|
||||
Where("id = ? AND updated_at = ?", cfg.ID, cfg.UpdatedAt).
|
||||
Select("*").Omit("id", "created_at").Updates(cfg)
|
||||
if res.Error != nil {
|
||||
return fmt.Errorf("save oci config snapshot: %w", res.Error)
|
||||
}
|
||||
if res.RowsAffected != 1 {
|
||||
return fmt.Errorf("save oci config snapshot: %w", gorm.ErrRecordNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applySuspension 查询账户能力接口:云端标记暂停的租户覆盖测活结论为 suspended。
|
||||
// 该接口对暂停/已终止租户仍可访问(常规 API 此时多被拒,会误判失联);查询失败不改变结论。
|
||||
func (s *OciConfigService) applySuspension(ctx context.Context, cfg *model.OciConfig, cred oci.Credentials) {
|
||||
caps, err := s.client.GetAccountCapabilities(ctx, cred, homeRegionOf(cfg))
|
||||
if err != nil || !caps.Suspended {
|
||||
return
|
||||
}
|
||||
cfg.AliveStatus = model.AliveStatusSuspended
|
||||
cfg.LastError = strings.TrimSpace("账户已被云端暂停 " + capsStatusNote(caps))
|
||||
}
|
||||
|
||||
// capsStatusNote 生成暂停详情备注(账户状态,如 terminated)。
|
||||
func capsStatusNote(caps oci.AccountCapabilities) string {
|
||||
if caps.AccountStatus == "" {
|
||||
return ""
|
||||
}
|
||||
return "(account status: " + caps.AccountStatus + ")"
|
||||
}
|
||||
|
||||
// applyProfile 拉取订阅信息;失败只把类别降级为 unknown,不影响测活结论。
|
||||
func (s *OciConfigService) applyProfile(ctx context.Context, cfg *model.OciConfig, cred oci.Credentials) {
|
||||
profile, err := s.client.FetchAccountProfile(ctx, cred)
|
||||
|
||||
Reference in New Issue
Block a user