package httpapi import ( "context" "errors" "os" "path/filepath" "strings" "time" "github.com/easyai/easyai-ai-gateway/apps/api/internal/config" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func (s *Server) startLocalTempAssetCleanup(ctx context.Context) { go func() { s.cleanupExpiredLocalTempAssets(ctx, time.Now()) ticker := time.NewTicker(time.Hour) defer ticker.Stop() for { select { case <-ctx.Done(): return case now := <-ticker.C: s.cleanupExpiredLocalTempAssets(ctx, now) } } }() } func (s *Server) cleanupExpiredLocalTempAssets(ctx context.Context, now time.Time) int { targets := []localTempAssetCleanupTarget{ { StorageDir: s.cfg.LocalGeneratedStorageDir, FallbackDir: config.DefaultLocalGeneratedStorageDir, MarkRequestAsset: false, }, { StorageDir: s.cfg.LocalUploadedStorageDir, FallbackDir: config.DefaultLocalUploadedStorageDir, MarkRequestAsset: true, }, } deleted := 0 for _, target := range targets { deleted += s.cleanupExpiredLocalTempAssetsInDir(ctx, now, target) } return deleted } type localTempAssetCleanupTarget struct { StorageDir string FallbackDir string MarkRequestAsset bool } func (s *Server) cleanupExpiredLocalTempAssetsInDir(ctx context.Context, now time.Time, target localTempAssetCleanupTarget) int { storageDir := strings.TrimSpace(target.StorageDir) if storageDir == "" { storageDir = target.FallbackDir } entries, err := os.ReadDir(storageDir) if err != nil { if !errors.Is(err, os.ErrNotExist) { s.logger.Warn("read local temp asset dir failed", "dir", storageDir, "error", err) } return 0 } ttl := time.Duration(s.localTempAssetTTLHours()) * time.Hour expiredBefore := now.Add(-ttl) deleted := 0 for _, entry := range entries { if entry.IsDir() { continue } info, err := entry.Info() if err != nil { continue } if info.ModTime().After(expiredBefore) { continue } localPath := filepath.Join(storageDir, entry.Name()) if err := os.Remove(localPath); err != nil && !errors.Is(err, os.ErrNotExist) { s.logger.Warn("remove local temp asset failed", "path", localPath, "error", err) continue } deleted++ if target.MarkRequestAsset && strings.HasPrefix(entry.Name(), requestAssetFilePrefix) && s.store != nil { if err := s.store.MarkRequestAssetExpiredByLocalPath(ctx, localPath, now); err != nil && !store.IsUndefinedDatabaseObject(err) { s.logger.Warn("mark local temp asset expired failed", "path", localPath, "error", err) } } } return deleted }